Austin Huang
3 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
76 changed files with 1047 additions and 1314 deletions
-
2app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
10app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
-
6app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java
-
57app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java
-
4app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
-
11app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
-
133app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
-
17app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
-
46app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
-
8app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java
-
11app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java
-
456app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
-
260app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.kt
-
18app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
30app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
15app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
-
162app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt
-
11app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
-
20app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
12app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt
-
1app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java
-
49app/src/main/java/awais/instagrabber/models/FollowModel.kt
-
4app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt
-
3app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt
-
19app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt
-
14app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt
-
2app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt
-
55app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java
-
60app/src/main/java/awais/instagrabber/utils/MediaUtils.java
-
19app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.java
-
167app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt
-
2app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt
-
2app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.java
-
39app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt
-
45app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt
-
94app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt
-
23app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java
-
10app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java
-
1app/src/main/res/layout/fragment_followers_viewer.xml
-
113app/src/main/res/layout/item_feed_bottom.xml
-
20app/src/main/res/layout/item_feed_photo.xml
-
20app/src/main/res/layout/item_feed_slider.xml
-
131app/src/main/res/layout/item_feed_top.xml
-
27app/src/main/res/layout/item_feed_video.xml
-
105app/src/main/res/layout/item_full_post_view.xml
-
1app/src/main/res/values-ar/strings.xml
-
1app/src/main/res/values-ca/strings.xml
-
1app/src/main/res/values-cs/strings.xml
-
1app/src/main/res/values-de/strings.xml
-
1app/src/main/res/values-el/strings.xml
-
1app/src/main/res/values-es/strings.xml
-
1app/src/main/res/values-eu/strings.xml
-
1app/src/main/res/values-fa/strings.xml
-
1app/src/main/res/values-fr/strings.xml
-
1app/src/main/res/values-hi/strings.xml
-
1app/src/main/res/values-in/strings.xml
-
1app/src/main/res/values-it/strings.xml
-
1app/src/main/res/values-ja/strings.xml
-
1app/src/main/res/values-ko/strings.xml
-
1app/src/main/res/values-mk/strings.xml
-
1app/src/main/res/values-nl/strings.xml
-
1app/src/main/res/values-or/strings.xml
-
1app/src/main/res/values-pl/strings.xml
-
1app/src/main/res/values-pt/strings.xml
-
1app/src/main/res/values-ru/strings.xml
-
1app/src/main/res/values-sk/strings.xml
-
1app/src/main/res/values-sv/strings.xml
-
1app/src/main/res/values-tr/strings.xml
-
1app/src/main/res/values-vi/strings.xml
-
1app/src/main/res/values-zh-rCN/strings.xml
-
1app/src/main/res/values-zh-rTW/strings.xml
-
7app/src/main/res/values/strings.xml
@ -1,456 +0,0 @@ |
|||||
package awais.instagrabber.fragments; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.content.res.Resources; |
|
||||
import android.os.Bundle; |
|
||||
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 android.widget.Toast; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.annotation.Nullable; |
|
||||
import androidx.appcompat.app.ActionBar; |
|
||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||
import androidx.appcompat.widget.SearchView; |
|
||||
import androidx.fragment.app.Fragment; |
|
||||
import androidx.navigation.NavDirections; |
|
||||
import androidx.navigation.fragment.NavHostFragment; |
|
||||
import androidx.recyclerview.widget.LinearLayoutManager; |
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
|
|
||||
import awais.instagrabber.R; |
|
||||
import awais.instagrabber.adapters.FollowAdapter; |
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|
||||
import awais.instagrabber.databinding.FragmentFollowersViewerBinding; |
|
||||
import awais.instagrabber.models.FollowModel; |
|
||||
import awais.instagrabber.repositories.responses.FriendshipListFetchResponse; |
|
||||
import awais.instagrabber.utils.AppExecutors; |
|
||||
import awais.instagrabber.utils.CoroutineUtilsKt; |
|
||||
import awais.instagrabber.utils.TextUtils; |
|
||||
import awais.instagrabber.webservices.FriendshipRepository; |
|
||||
import awais.instagrabber.webservices.ServiceCallback; |
|
||||
import kotlinx.coroutines.Dispatchers; |
|
||||
import thoughtbot.expandableadapter.ExpandableGroup; |
|
||||
|
|
||||
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { |
|
||||
private static final String TAG = "FollowViewerFragment"; |
|
||||
|
|
||||
private final ArrayList<FollowModel> followModels = new ArrayList<>(); |
|
||||
private final ArrayList<FollowModel> followingModels = new ArrayList<>(); |
|
||||
private final ArrayList<FollowModel> followersModels = new ArrayList<>(); |
|
||||
private final ArrayList<FollowModel> allFollowing = new ArrayList<>(); |
|
||||
|
|
||||
private boolean moreAvailable = true, isFollowersList, isCompare = false, loading = false, shouldRefresh = true, searching = false; |
|
||||
private long profileId; |
|
||||
private String username; |
|
||||
private String namePost; |
|
||||
private String type; |
|
||||
private String endCursor; |
|
||||
private Resources resources; |
|
||||
private LinearLayoutManager layoutManager; |
|
||||
private RecyclerLazyLoader lazyLoader; |
|
||||
private FollowModel model; |
|
||||
private FollowAdapter adapter; |
|
||||
private View.OnClickListener clickListener; |
|
||||
private FragmentFollowersViewerBinding binding; |
|
||||
private SwipeRefreshLayout root; |
|
||||
private FriendshipRepository friendshipRepository; |
|
||||
private AppCompatActivity fragmentActivity; |
|
||||
|
|
||||
final ServiceCallback<FriendshipListFetchResponse> followingFetchCb = new ServiceCallback<FriendshipListFetchResponse>() { |
|
||||
@Override |
|
||||
public void onSuccess(final FriendshipListFetchResponse result) { |
|
||||
if (result != null && isCompare) { |
|
||||
followingModels.addAll(result.getItems()); |
|
||||
if (!isFollowersList) followModels.addAll(result.getItems()); |
|
||||
if (result.isMoreAvailable()) { |
|
||||
endCursor = result.getNextMaxId(); |
|
||||
friendshipRepository.getList( |
|
||||
false, |
|
||||
profileId, |
|
||||
endCursor, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else if (followersModels.size() == 0) { |
|
||||
if (!isFollowersList) moreAvailable = false; |
|
||||
friendshipRepository.getList( |
|
||||
true, |
|
||||
profileId, |
|
||||
null, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
followingFetchCb.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
followingFetchCb.onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else { |
|
||||
if (!isFollowersList) moreAvailable = false; |
|
||||
showCompare(); |
|
||||
} |
|
||||
} else if (isCompare) binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
try { |
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show(); |
|
||||
} catch (Throwable ignored) {} |
|
||||
Log.e(TAG, "Error fetching list (double, following)", t); |
|
||||
} |
|
||||
}; |
|
||||
final ServiceCallback<FriendshipListFetchResponse> followersFetchCb = new ServiceCallback<FriendshipListFetchResponse>() { |
|
||||
@Override |
|
||||
public void onSuccess(final FriendshipListFetchResponse result) { |
|
||||
if (result != null && isCompare) { |
|
||||
followersModels.addAll(result.getItems()); |
|
||||
if (isFollowersList) followModels.addAll(result.getItems()); |
|
||||
if (result.isMoreAvailable()) { |
|
||||
endCursor = result.getNextMaxId(); |
|
||||
friendshipRepository.getList( |
|
||||
true, |
|
||||
profileId, |
|
||||
endCursor, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else if (followingModels.size() == 0) { |
|
||||
if (isFollowersList) moreAvailable = false; |
|
||||
friendshipRepository.getList( |
|
||||
false, |
|
||||
profileId, |
|
||||
null, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
followingFetchCb.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
followingFetchCb.onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else { |
|
||||
if (isFollowersList) moreAvailable = false; |
|
||||
showCompare(); |
|
||||
} |
|
||||
} else if (isCompare) binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
try { |
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show(); |
|
||||
} catch (Throwable ignored) {} |
|
||||
Log.e(TAG, "Error fetching list (double, follower)", t); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
@Override |
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|
||||
super.onCreate(savedInstanceState); |
|
||||
friendshipRepository = FriendshipRepository.Companion.getInstance(); |
|
||||
fragmentActivity = (AppCompatActivity) getActivity(); |
|
||||
setHasOptionsMenu(true); |
|
||||
} |
|
||||
|
|
||||
@NonNull |
|
||||
@Override |
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
|
||||
if (root != null) { |
|
||||
shouldRefresh = false; |
|
||||
return root; |
|
||||
} |
|
||||
binding = FragmentFollowersViewerBinding.inflate(getLayoutInflater()); |
|
||||
root = binding.getRoot(); |
|
||||
return root; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|
||||
if (!shouldRefresh) return; |
|
||||
init(); |
|
||||
shouldRefresh = false; |
|
||||
} |
|
||||
|
|
||||
private void init() { |
|
||||
if (getArguments() == null) return; |
|
||||
final FollowViewerFragmentArgs fragmentArgs = FollowViewerFragmentArgs.fromBundle(getArguments()); |
|
||||
profileId = fragmentArgs.getProfileId(); |
|
||||
isFollowersList = fragmentArgs.getIsFollowersList(); |
|
||||
username = fragmentArgs.getUsername(); |
|
||||
namePost = username; |
|
||||
if (TextUtils.isEmpty(username)) { |
|
||||
// this usually should not occur |
|
||||
username = "You"; |
|
||||
namePost = "You're"; |
|
||||
} |
|
||||
setTitle(username); |
|
||||
resources = getResources(); |
|
||||
clickListener = v -> { |
|
||||
final Object tag = v.getTag(); |
|
||||
if (tag instanceof FollowModel) { |
|
||||
model = (FollowModel) tag; |
|
||||
try { |
|
||||
final NavDirections action = FollowViewerFragmentDirections.actionToProfile().setUsername(model.getUsername()); |
|
||||
NavHostFragment.findNavController(this).navigate(action); |
|
||||
} catch (Exception e) { |
|
||||
Log.e(TAG, "init: ", e); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this); |
|
||||
onRefresh(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onResume() { |
|
||||
super.onResume(); |
|
||||
setTitle(username); |
|
||||
setSubtitle(type); |
|
||||
} |
|
||||
|
|
||||
private void setTitle(final String title) { |
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|
||||
if (actionBar == null) return; |
|
||||
actionBar.setTitle(title); |
|
||||
} |
|
||||
|
|
||||
private void setSubtitle(final String subtitle) { |
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|
||||
if (actionBar == null) return; |
|
||||
actionBar.setSubtitle(subtitle); |
|
||||
} |
|
||||
|
|
||||
private void setSubtitle(@SuppressWarnings("SameParameterValue") final int subtitleRes) { |
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|
||||
if (actionBar == null) return; |
|
||||
actionBar.setSubtitle(subtitleRes); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onRefresh() { |
|
||||
if (isCompare) listCompare(); |
|
||||
else listFollows(); |
|
||||
endCursor = null; |
|
||||
lazyLoader.resetState(); |
|
||||
} |
|
||||
|
|
||||
private void listFollows() { |
|
||||
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following); |
|
||||
setSubtitle(type); |
|
||||
final ServiceCallback<FriendshipListFetchResponse> cb = new ServiceCallback<FriendshipListFetchResponse>() { |
|
||||
@Override |
|
||||
public void onSuccess(final FriendshipListFetchResponse result) { |
|
||||
if (result == null) { |
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
return; |
|
||||
} |
|
||||
int oldSize = followModels.size() == 0 ? 0 : followModels.size() - 1; |
|
||||
followModels.addAll(result.getItems()); |
|
||||
if (result.isMoreAvailable()) { |
|
||||
moreAvailable = true; |
|
||||
endCursor = result.getNextMaxId(); |
|
||||
} else moreAvailable = false; |
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
if (isFollowersList) followersModels.addAll(result.getItems()); |
|
||||
else followingModels.addAll(result.getItems()); |
|
||||
refreshAdapter(followModels, null, null, null); |
|
||||
layoutManager.scrollToPosition(oldSize); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onFailure(final Throwable t) { |
|
||||
try { |
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show(); |
|
||||
} catch (Throwable ignored) {} |
|
||||
Log.e(TAG, "Error fetching list (single)", t); |
|
||||
} |
|
||||
}; |
|
||||
layoutManager = new LinearLayoutManager(getContext()); |
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|
||||
if (!TextUtils.isEmpty(endCursor) && !searching) { |
|
||||
binding.swipeRefreshLayout.setRefreshing(true); |
|
||||
layoutManager.setStackFromEnd(true); |
|
||||
friendshipRepository.getList( |
|
||||
isFollowersList, |
|
||||
profileId, |
|
||||
endCursor, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
cb.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
cb.onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
endCursor = null; |
|
||||
} |
|
||||
}); |
|
||||
binding.rvFollow.addOnScrollListener(lazyLoader); |
|
||||
binding.rvFollow.setLayoutManager(layoutManager); |
|
||||
if (moreAvailable) { |
|
||||
binding.swipeRefreshLayout.setRefreshing(true); |
|
||||
friendshipRepository.getList( |
|
||||
isFollowersList, |
|
||||
profileId, |
|
||||
endCursor, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
if (throwable != null) { |
|
||||
cb.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
cb.onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else { |
|
||||
refreshAdapter(followModels, null, null, null); |
|
||||
layoutManager.scrollToPosition(0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void listCompare() { |
|
||||
layoutManager.setStackFromEnd(false); |
|
||||
binding.rvFollow.clearOnScrollListeners(); |
|
||||
loading = true; |
|
||||
setSubtitle(R.string.followers_compare); |
|
||||
allFollowing.clear(); |
|
||||
if (moreAvailable) { |
|
||||
binding.swipeRefreshLayout.setRefreshing(true); |
|
||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show(); |
|
||||
friendshipRepository.getList( |
|
||||
isFollowersList, |
|
||||
profileId, |
|
||||
endCursor, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
final ServiceCallback<FriendshipListFetchResponse> callback = isFollowersList ? followersFetchCb : followingFetchCb; |
|
||||
if (throwable != null) { |
|
||||
callback.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
callback.onSuccess(response); |
|
||||
}), Dispatchers.getIO()) |
|
||||
); |
|
||||
} else if (followersModels.size() == 0 || followingModels.size() == 0) { |
|
||||
binding.swipeRefreshLayout.setRefreshing(true); |
|
||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show(); |
|
||||
friendshipRepository.getList( |
|
||||
!isFollowersList, |
|
||||
profileId, |
|
||||
null, |
|
||||
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { |
|
||||
final ServiceCallback<FriendshipListFetchResponse> callback = isFollowersList ? followingFetchCb : followersFetchCb; |
|
||||
if (throwable != null) { |
|
||||
callback.onFailure(throwable); |
|
||||
return; |
|
||||
} |
|
||||
callback.onSuccess(response); |
|
||||
}), Dispatchers.getIO())); |
|
||||
} else showCompare(); |
|
||||
} |
|
||||
|
|
||||
private void showCompare() { |
|
||||
allFollowing.addAll(followersModels); |
|
||||
allFollowing.retainAll(followingModels); |
|
||||
|
|
||||
for (final FollowModel followModel : allFollowing) { |
|
||||
followersModels.remove(followModel); |
|
||||
followingModels.remove(followModel); |
|
||||
} |
|
||||
|
|
||||
allFollowing.trimToSize(); |
|
||||
followersModels.trimToSize(); |
|
||||
followingModels.trimToSize(); |
|
||||
|
|
||||
binding.swipeRefreshLayout.setRefreshing(false); |
|
||||
|
|
||||
refreshAdapter(null, followingModels, followersModels, allFollowing); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater inflater) { |
|
||||
inflater.inflate(R.menu.follow, menu); |
|
||||
final MenuItem menuSearch = menu.findItem(R.id.action_search); |
|
||||
final SearchView searchView = (SearchView) menuSearch.getActionView(); |
|
||||
searchView.setQueryHint(getResources().getString(R.string.action_search)); |
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { |
|
||||
|
|
||||
@Override |
|
||||
public boolean onQueryTextSubmit(final String query) { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public boolean onQueryTextChange(final String query) { |
|
||||
if (TextUtils.isEmpty(query)) { |
|
||||
searching = false; |
|
||||
// refreshAdapter(followModels, followingModels, followersModels, allFollowing); |
|
||||
} |
|
||||
// else filter.filter(query.toLowerCase()); |
|
||||
if (adapter != null) { |
|
||||
searching = true; |
|
||||
adapter.getFilter().filter(query); |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) { |
|
||||
if (item.getItemId() != R.id.action_compare) return super.onOptionsItemSelected(item); |
|
||||
binding.rvFollow.setAdapter(null); |
|
||||
final Context context = getContext(); |
|
||||
if (loading) Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_LONG).show(); |
|
||||
else if (isCompare) { |
|
||||
isCompare = false; |
|
||||
listFollows(); |
|
||||
} else { |
|
||||
isCompare = true; |
|
||||
listCompare(); |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
private void refreshAdapter(final ArrayList<FollowModel> followModels, |
|
||||
final ArrayList<FollowModel> followingModels, |
|
||||
final ArrayList<FollowModel> followersModels, |
|
||||
final ArrayList<FollowModel> allFollowing) { |
|
||||
loading = false; |
|
||||
final ArrayList<ExpandableGroup> groups = new ArrayList<>(1); |
|
||||
|
|
||||
if (isCompare && followingModels != null && followersModels != null && allFollowing != null) { |
|
||||
if (followingModels.size() > 0) |
|
||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_following, username), followingModels)); |
|
||||
if (followersModels.size() > 0) |
|
||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_follower, namePost), followersModels)); |
|
||||
if (allFollowing.size() > 0) |
|
||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_both_following), allFollowing)); |
|
||||
} else if (followModels != null) { |
|
||||
groups.add(new ExpandableGroup(type, followModels)); |
|
||||
} else return; |
|
||||
adapter = new FollowAdapter(clickListener, groups); |
|
||||
adapter.toggleGroup(0); |
|
||||
binding.rvFollow.setAdapter(adapter); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,260 @@ |
|||||
|
package awais.instagrabber.fragments |
||||
|
|
||||
|
import android.content.Context |
||||
|
import android.content.res.Resources |
||||
|
import android.os.Bundle |
||||
|
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 android.widget.Toast |
||||
|
|
||||
|
import androidx.annotation.NonNull |
||||
|
import androidx.annotation.Nullable |
||||
|
import androidx.appcompat.app.ActionBar |
||||
|
import androidx.appcompat.app.AppCompatActivity |
||||
|
import androidx.appcompat.widget.SearchView |
||||
|
import androidx.fragment.app.Fragment |
||||
|
import androidx.lifecycle.ViewModelProvider |
||||
|
import androidx.navigation.fragment.NavHostFragment |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager |
||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||
|
|
||||
|
import java.util.ArrayList |
||||
|
|
||||
|
import awais.instagrabber.R |
||||
|
import awais.instagrabber.adapters.FollowAdapter |
||||
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader |
||||
|
import awais.instagrabber.databinding.FragmentFollowersViewerBinding |
||||
|
import awais.instagrabber.models.Resource |
||||
|
import awais.instagrabber.repositories.responses.User |
||||
|
import awais.instagrabber.utils.AppExecutors |
||||
|
import awais.instagrabber.viewmodels.FollowViewModel |
||||
|
import thoughtbot.expandableadapter.ExpandableGroup |
||||
|
|
||||
|
class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener { |
||||
|
private val followModels: ArrayList<User> = ArrayList<User>() |
||||
|
private val followingModels: ArrayList<User> = ArrayList<User>() |
||||
|
private val followersModels: ArrayList<User> = ArrayList<User>() |
||||
|
private val allFollowing: ArrayList<User> = ArrayList<User>() |
||||
|
private val moreAvailable = true |
||||
|
private var isFollowersList = false |
||||
|
private var isCompare = false |
||||
|
private var shouldRefresh = true |
||||
|
private var searching = false |
||||
|
private var profileId: Long = 0 |
||||
|
private var username: String? = null |
||||
|
private var namePost: String? = null |
||||
|
private var type = 0 |
||||
|
private var root: SwipeRefreshLayout? = null |
||||
|
private var adapter: FollowAdapter? = null |
||||
|
private lateinit var lazyLoader: RecyclerLazyLoader |
||||
|
private lateinit var fragmentActivity: AppCompatActivity |
||||
|
private lateinit var viewModel: FollowViewModel |
||||
|
private lateinit var binding: FragmentFollowersViewerBinding |
||||
|
|
||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||
|
super.onCreate(savedInstanceState) |
||||
|
fragmentActivity = activity as AppCompatActivity |
||||
|
viewModel = ViewModelProvider(this).get(FollowViewModel::class.java) |
||||
|
setHasOptionsMenu(true) |
||||
|
} |
||||
|
|
||||
|
override fun onCreateView( |
||||
|
inflater: LayoutInflater, |
||||
|
container: ViewGroup?, |
||||
|
savedInstanceState: Bundle? |
||||
|
): View { |
||||
|
if (root != null) { |
||||
|
shouldRefresh = false |
||||
|
return root!! |
||||
|
} |
||||
|
binding = FragmentFollowersViewerBinding.inflate(layoutInflater) |
||||
|
root = binding.root |
||||
|
return root!! |
||||
|
} |
||||
|
|
||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
||||
|
if (!shouldRefresh) return |
||||
|
init() |
||||
|
shouldRefresh = false |
||||
|
} |
||||
|
|
||||
|
private fun init() { |
||||
|
val args = arguments ?: return |
||||
|
val fragmentArgs = FollowViewerFragmentArgs.fromBundle(args) |
||||
|
viewModel.userId.value = fragmentArgs.profileId |
||||
|
isFollowersList = fragmentArgs.isFollowersList |
||||
|
username = fragmentArgs.username |
||||
|
namePost = username |
||||
|
setTitle(username) |
||||
|
binding.swipeRefreshLayout.setOnRefreshListener(this) |
||||
|
if (isCompare) listCompare() else listFollows() |
||||
|
viewModel.fetch(isFollowersList, null) |
||||
|
} |
||||
|
|
||||
|
override fun onResume() { |
||||
|
super.onResume() |
||||
|
setTitle(username) |
||||
|
setSubtitle(type) |
||||
|
} |
||||
|
|
||||
|
private fun setTitle(title: String?) { |
||||
|
val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return |
||||
|
actionBar.title = title |
||||
|
} |
||||
|
|
||||
|
private fun setSubtitle(subtitleRes: Int) { |
||||
|
val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return |
||||
|
actionBar.setSubtitle(subtitleRes) |
||||
|
} |
||||
|
|
||||
|
override fun onRefresh() { |
||||
|
lazyLoader.resetState() |
||||
|
viewModel.clearProgress() |
||||
|
if (isCompare) listCompare() |
||||
|
else viewModel.fetch(isFollowersList, null) |
||||
|
} |
||||
|
|
||||
|
private fun listFollows() { |
||||
|
viewModel.comparison.removeObservers(viewLifecycleOwner) |
||||
|
viewModel.status.removeObservers(viewLifecycleOwner) |
||||
|
type = if (isFollowersList) R.string.followers_type_followers else R.string.followers_type_following |
||||
|
setSubtitle(type) |
||||
|
val layoutManager = LinearLayoutManager(context) |
||||
|
lazyLoader = RecyclerLazyLoader(layoutManager, { _, totalItemsCount -> |
||||
|
binding.swipeRefreshLayout.isRefreshing = true |
||||
|
val liveData = if (searching) viewModel.search(isFollowersList) |
||||
|
else viewModel.fetch(isFollowersList, null) |
||||
|
liveData.observe(viewLifecycleOwner) { |
||||
|
binding.swipeRefreshLayout.isRefreshing = it.status != Resource.Status.SUCCESS |
||||
|
layoutManager.scrollToPosition(totalItemsCount) |
||||
|
} |
||||
|
}) |
||||
|
binding.rvFollow.addOnScrollListener(lazyLoader) |
||||
|
binding.rvFollow.layoutManager = layoutManager |
||||
|
viewModel.getList(isFollowersList).observe(viewLifecycleOwner) { |
||||
|
binding.swipeRefreshLayout.isRefreshing = false |
||||
|
refreshAdapter(it, null, null, null) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private fun listCompare() { |
||||
|
viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner) |
||||
|
binding.rvFollow.clearOnScrollListeners() |
||||
|
binding.swipeRefreshLayout.isRefreshing = true |
||||
|
setSubtitle(R.string.followers_compare) |
||||
|
viewModel.status.observe(viewLifecycleOwner) {} |
||||
|
viewModel.comparison.observe(viewLifecycleOwner) { |
||||
|
if (it != null) { |
||||
|
binding.swipeRefreshLayout.isRefreshing = false |
||||
|
refreshAdapter(null, it.first, it.second, it.third) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { |
||||
|
inflater.inflate(R.menu.follow, menu) |
||||
|
val menuSearch = menu.findItem(R.id.action_search) |
||||
|
val searchView = menuSearch.actionView as SearchView |
||||
|
searchView.queryHint = resources.getString(R.string.action_search) |
||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { |
||||
|
override fun onQueryTextSubmit(query: String): Boolean { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
override fun onQueryTextChange(query: String): Boolean { |
||||
|
if (query.isNullOrEmpty()) { |
||||
|
if (!isCompare && searching) { |
||||
|
viewModel.setQuery(null, isFollowersList) |
||||
|
viewModel.getSearch().removeObservers(viewLifecycleOwner) |
||||
|
viewModel.getList(isFollowersList).observe(viewLifecycleOwner) { |
||||
|
refreshAdapter(it, null, null, null) |
||||
|
} |
||||
|
} |
||||
|
searching = false |
||||
|
return true |
||||
|
} |
||||
|
searching = true |
||||
|
if (isCompare && adapter != null) { |
||||
|
adapter!!.filter.filter(query) |
||||
|
return true |
||||
|
} |
||||
|
viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner) |
||||
|
binding.swipeRefreshLayout.isRefreshing = true |
||||
|
viewModel.setQuery(query, isFollowersList) |
||||
|
viewModel.getSearch().observe(viewLifecycleOwner) { |
||||
|
binding.swipeRefreshLayout.isRefreshing = false |
||||
|
refreshAdapter(it, null, null, null) |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean { |
||||
|
if (item.itemId != R.id.action_compare) return super.onOptionsItemSelected(item) |
||||
|
binding.rvFollow.adapter = null |
||||
|
if (isCompare) { |
||||
|
isCompare = false |
||||
|
listFollows() |
||||
|
} else { |
||||
|
isCompare = true |
||||
|
listCompare() |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
private fun refreshAdapter( |
||||
|
followModels: List<User>?, |
||||
|
allFollowing: List<User>?, |
||||
|
followingModels: List<User>?, |
||||
|
followersModels: List<User>? |
||||
|
) { |
||||
|
val groups: ArrayList<ExpandableGroup> = ArrayList<ExpandableGroup>(1) |
||||
|
if (isCompare && followingModels != null && followersModels != null && allFollowing != null) { |
||||
|
if (followingModels.size > 0) groups.add( |
||||
|
ExpandableGroup( |
||||
|
getString( |
||||
|
R.string.followers_not_following, |
||||
|
username |
||||
|
), followingModels |
||||
|
) |
||||
|
) |
||||
|
if (followersModels.size > 0) groups.add( |
||||
|
ExpandableGroup( |
||||
|
getString( |
||||
|
R.string.followers_not_follower, |
||||
|
namePost |
||||
|
), followersModels |
||||
|
) |
||||
|
) |
||||
|
if (allFollowing.size > 0) groups.add( |
||||
|
ExpandableGroup( |
||||
|
getString(R.string.followers_both_following), |
||||
|
allFollowing |
||||
|
) |
||||
|
) |
||||
|
} else if (followModels != null) { |
||||
|
groups.add(ExpandableGroup(getString(type), followModels)) |
||||
|
} else return |
||||
|
adapter = FollowAdapter({ v -> |
||||
|
val tag = v.tag |
||||
|
if (tag is User) { |
||||
|
val model = tag |
||||
|
val bundle = Bundle() |
||||
|
bundle.putString("username", model.username) |
||||
|
NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle) |
||||
|
} |
||||
|
}, groups) |
||||
|
adapter!!.toggleGroup(0) |
||||
|
binding.rvFollow.adapter = adapter!! |
||||
|
} |
||||
|
|
||||
|
companion object { |
||||
|
private const val TAG = "FollowViewerFragment" |
||||
|
} |
||||
|
} |
@ -1,49 +0,0 @@ |
|||||
package awais.instagrabber.models |
|
||||
|
|
||||
import java.io.Serializable |
|
||||
|
|
||||
class FollowModel( |
|
||||
val id: String, |
|
||||
val username: String, |
|
||||
val fullName: String, |
|
||||
val profilePicUrl: String |
|
||||
) : Serializable { |
|
||||
private var hasNextPage = false |
|
||||
get() = endCursor != null && field |
|
||||
|
|
||||
var isShown = true |
|
||||
|
|
||||
var endCursor: String? = null |
|
||||
private set |
|
||||
|
|
||||
fun setPageCursor(hasNextPage: Boolean, endCursor: String?) { |
|
||||
this.endCursor = endCursor |
|
||||
this.hasNextPage = hasNextPage |
|
||||
} |
|
||||
|
|
||||
override fun equals(other: Any?): Boolean { |
|
||||
if (this === other) return true |
|
||||
if (javaClass != other?.javaClass) return false |
|
||||
|
|
||||
other as FollowModel |
|
||||
|
|
||||
if (id != other.id) return false |
|
||||
if (username != other.username) return false |
|
||||
if (fullName != other.fullName) return false |
|
||||
if (profilePicUrl != other.profilePicUrl) return false |
|
||||
if (isShown != other.isShown) return false |
|
||||
if (endCursor != other.endCursor) return false |
|
||||
|
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
override fun hashCode(): Int { |
|
||||
var result = id.hashCode() |
|
||||
result = 31 * result + username.hashCode() |
|
||||
result = 31 * result + fullName.hashCode() |
|
||||
result = 31 * result + profilePicUrl.hashCode() |
|
||||
result = 31 * result + isShown.hashCode() |
|
||||
result = 31 * result + (endCursor?.hashCode() ?: 0) |
|
||||
return result |
|
||||
} |
|
||||
} |
|
@ -1,27 +1,10 @@ |
|||||
package awais.instagrabber.repositories.responses |
package awais.instagrabber.repositories.responses |
||||
|
|
||||
import awais.instagrabber.models.FollowModel |
|
||||
|
|
||||
data class FriendshipListFetchResponse( |
data class FriendshipListFetchResponse( |
||||
var nextMaxId: String?, |
var nextMaxId: String?, |
||||
var status: String?, |
var status: String?, |
||||
var items: List<FollowModel>? |
|
||||
|
var users: List<User>? |
||||
) { |
) { |
||||
val isMoreAvailable: Boolean |
val isMoreAvailable: Boolean |
||||
get() = !nextMaxId.isNullOrBlank() |
get() = !nextMaxId.isNullOrBlank() |
||||
|
|
||||
fun setNextMaxId(nextMaxId: String): FriendshipListFetchResponse { |
|
||||
this.nextMaxId = nextMaxId |
|
||||
return this |
|
||||
} |
|
||||
|
|
||||
fun setStatus(status: String): FriendshipListFetchResponse { |
|
||||
this.status = status |
|
||||
return this |
|
||||
} |
|
||||
|
|
||||
fun setItems(items: List<FollowModel>): FriendshipListFetchResponse { |
|
||||
this.items = items |
|
||||
return this |
|
||||
} |
|
||||
} |
} |
@ -1,9 +1,9 @@ |
|||||
package awais.instagrabber.repositories.responses.notification |
package awais.instagrabber.repositories.responses.notification |
||||
|
|
||||
class NotificationCounts(val commentLikesCount: Int, |
|
||||
val userTagsCount: Int, |
|
||||
val likesCount: Int, |
|
||||
val commentsCount: Int, |
|
||||
val relationshipsCount: Int, |
|
||||
val pOYCount: Int, |
|
||||
val requestsCount: Int) |
|
||||
|
class NotificationCounts(val commentLikes: Int, |
||||
|
val usertags: Int, |
||||
|
val likes: Int, |
||||
|
val comments: Int, |
||||
|
val relationships: Int, |
||||
|
val photosOfYou: Int, |
||||
|
val requests: Int) |
@ -1,19 +0,0 @@ |
|||||
package awais.instagrabber.viewmodels; |
|
||||
|
|
||||
import androidx.lifecycle.MutableLiveData; |
|
||||
import androidx.lifecycle.ViewModel; |
|
||||
|
|
||||
import java.util.List; |
|
||||
|
|
||||
import awais.instagrabber.models.FollowModel; |
|
||||
|
|
||||
public class FollowViewModel extends ViewModel { |
|
||||
private MutableLiveData<List<FollowModel>> list; |
|
||||
|
|
||||
public MutableLiveData<List<FollowModel>> getList() { |
|
||||
if (list == null) { |
|
||||
list = new MutableLiveData<>(); |
|
||||
} |
|
||||
return list; |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,167 @@ |
|||||
|
package awais.instagrabber.viewmodels |
||||
|
|
||||
|
import androidx.lifecycle.LiveData |
||||
|
import androidx.lifecycle.MediatorLiveData |
||||
|
import androidx.lifecycle.MutableLiveData |
||||
|
import androidx.lifecycle.ViewModel |
||||
|
import androidx.lifecycle.viewModelScope |
||||
|
import awais.instagrabber.models.Resource |
||||
|
import awais.instagrabber.repositories.responses.User |
||||
|
import awais.instagrabber.webservices.FriendshipRepository |
||||
|
import kotlinx.coroutines.Dispatchers |
||||
|
import kotlinx.coroutines.launch |
||||
|
|
||||
|
class FollowViewModel : ViewModel() { |
||||
|
// data |
||||
|
val userId = MutableLiveData<Long>() |
||||
|
private val followers = MutableLiveData<List<User>>() |
||||
|
private val followings = MutableLiveData<List<User>>() |
||||
|
private val searchResults = MutableLiveData<List<User>>() |
||||
|
|
||||
|
// cursors |
||||
|
private val followersMaxId = MutableLiveData<String?>("") |
||||
|
private val followingMaxId = MutableLiveData<String?>("") |
||||
|
private val searchingMaxId = MutableLiveData<String?>("") |
||||
|
private val searchQuery = MutableLiveData<String?>() |
||||
|
|
||||
|
// comparison |
||||
|
val status: LiveData<Pair<Boolean, Boolean>> = object : MediatorLiveData<Pair<Boolean, Boolean>>() { |
||||
|
init { |
||||
|
postValue(Pair(false, false)) |
||||
|
addSource(followersMaxId) { |
||||
|
if (it == null) { |
||||
|
postValue(Pair(true, value!!.second)) |
||||
|
} |
||||
|
else fetch(true, it) |
||||
|
} |
||||
|
addSource(followingMaxId) { |
||||
|
if (it == null) { |
||||
|
postValue(Pair(value!!.first, true)) |
||||
|
} |
||||
|
else fetch(false, it) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
val comparison: LiveData<Triple<List<User>, List<User>, List<User>>> = |
||||
|
object : MediatorLiveData<Triple<List<User>, List<User>, List<User>>>() { |
||||
|
init { |
||||
|
addSource(status) { |
||||
|
if (it.first && it.second) { |
||||
|
val followersList = followers.value!! |
||||
|
val followingList = followings.value!! |
||||
|
val allUsers: MutableList<User> = mutableListOf() |
||||
|
allUsers.addAll(followersList) |
||||
|
allUsers.addAll(followingList) |
||||
|
val followersMap = followersList.groupBy { it.pk } |
||||
|
val followingMap = followingList.groupBy { it.pk } |
||||
|
val mutual: MutableList<User> = mutableListOf() |
||||
|
val onlyFollowing: MutableList<User> = mutableListOf() |
||||
|
val onlyFollowers: MutableList<User> = mutableListOf() |
||||
|
allUsers.forEach { |
||||
|
val isFollowing = followingMap.get(it.pk) != null |
||||
|
val isFollower = followersMap.get(it.pk) != null |
||||
|
if (isFollowing && isFollower) mutual.add(it) |
||||
|
else if (isFollowing) onlyFollowing.add(it) |
||||
|
else if (isFollower) onlyFollowers.add(it) |
||||
|
} |
||||
|
postValue(Triple(mutual, onlyFollowing, onlyFollowers)) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private val friendshipRepository: FriendshipRepository by lazy { FriendshipRepository.getInstance() } |
||||
|
|
||||
|
// fetch: supply max ID for continuous fetch |
||||
|
fun fetch(follower: Boolean, nextMaxId: String?): LiveData<Resource<Any?>> { |
||||
|
val data = MutableLiveData<Resource<Any?>>() |
||||
|
data.postValue(Resource.loading(null)) |
||||
|
val maxId = if (follower) followersMaxId else followingMaxId |
||||
|
if (maxId.value == null && nextMaxId == null) data.postValue(Resource.success(null)) |
||||
|
else if (userId.value == null) data.postValue(Resource.error("No user ID supplied!", null)) |
||||
|
else viewModelScope.launch(Dispatchers.IO) { |
||||
|
try { |
||||
|
val tempList = friendshipRepository.getList( |
||||
|
follower, |
||||
|
userId.value!!, |
||||
|
nextMaxId ?: maxId.value, |
||||
|
null |
||||
|
) |
||||
|
if (!tempList.status.equals("ok")) { |
||||
|
data.postValue(Resource.error("Status not ok!", null)) |
||||
|
} |
||||
|
else { |
||||
|
if (tempList.users != null) { |
||||
|
val liveData = if (follower) followers else followings |
||||
|
val currentList = if (liveData.value != null) liveData.value!!.toMutableList() |
||||
|
else mutableListOf() |
||||
|
currentList.addAll(tempList.users!!) |
||||
|
liveData.postValue(currentList.toList()) |
||||
|
} |
||||
|
maxId.postValue(tempList.nextMaxId) |
||||
|
data.postValue(Resource.success(null)) |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
data.postValue(Resource.error(e.message, null)) |
||||
|
} |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
fun getList(follower: Boolean): LiveData<List<User>> { |
||||
|
return if (follower) followers else followings |
||||
|
} |
||||
|
|
||||
|
fun search(follower: Boolean): LiveData<Resource<Any?>> { |
||||
|
val data = MutableLiveData<Resource<Any?>>() |
||||
|
data.postValue(Resource.loading(null)) |
||||
|
val query = searchQuery.value |
||||
|
if (searchingMaxId.value == null) data.postValue(Resource.success(null)) |
||||
|
else if (userId.value == null) data.postValue(Resource.error("No user ID supplied!", null)) |
||||
|
else if (query.isNullOrEmpty()) data.postValue(Resource.error("No query supplied!", null)) |
||||
|
else viewModelScope.launch(Dispatchers.IO) { |
||||
|
try { |
||||
|
val tempList = friendshipRepository.getList( |
||||
|
follower, |
||||
|
userId.value!!, |
||||
|
searchingMaxId.value, |
||||
|
query |
||||
|
) |
||||
|
if (!tempList.status.equals("ok")) { |
||||
|
data.postValue(Resource.error("Status not ok!", null)) |
||||
|
} |
||||
|
else { |
||||
|
if (tempList.users != null) { |
||||
|
val currentList = if (searchResults.value != null) searchResults.value!!.toMutableList() |
||||
|
else mutableListOf() |
||||
|
currentList.addAll(tempList.users!!) |
||||
|
searchResults.postValue(currentList.toList()) |
||||
|
} |
||||
|
searchingMaxId.postValue(tempList.nextMaxId) |
||||
|
data.postValue(Resource.success(null)) |
||||
|
} |
||||
|
} catch (e: Exception) { |
||||
|
data.postValue(Resource.error(e.message, null)) |
||||
|
} |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
fun getSearch(): LiveData<List<User>> { |
||||
|
return searchResults |
||||
|
} |
||||
|
|
||||
|
fun setQuery(query: String?, follower: Boolean) { |
||||
|
searchQuery.value = query |
||||
|
if (!query.isNullOrEmpty()) search(follower) |
||||
|
} |
||||
|
|
||||
|
fun clearProgress() { |
||||
|
followersMaxId.value = "" |
||||
|
followingMaxId.value = "" |
||||
|
searchingMaxId.value = "" |
||||
|
followings.value = listOf<User>() |
||||
|
followers.value = listOf<User>() |
||||
|
searchResults.value = listOf<User>() |
||||
|
} |
||||
|
} |
@ -1,113 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<LinearLayout 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="wrap_content" |
|
||||
android:orientation="vertical"> |
|
||||
|
|
||||
<LinearLayout |
|
||||
android:id="@+id/top_container" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:orientation="horizontal"> |
|
||||
|
|
||||
<LinearLayout |
|
||||
android:id="@+id/btnComments" |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="match_parent" |
|
||||
android:background="?android:selectableItemBackground" |
|
||||
android:gravity="center" |
|
||||
android:orientation="horizontal" |
|
||||
android:padding="4dp"> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatImageView |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:padding="4dp" |
|
||||
app:srcCompat="@drawable/ic_outline_comments_24" |
|
||||
app:tint="?android:textColorPrimary" /> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/commentsCount" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="match_parent" |
|
||||
android:gravity="center_vertical" |
|
||||
android:textAppearance="?attr/textAppearanceButton" |
|
||||
tools:text="690000" /> |
|
||||
</LinearLayout> |
|
||||
|
|
||||
<LinearLayout |
|
||||
android:id="@+id/btnViews" |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="match_parent" |
|
||||
android:gravity="center" |
|
||||
android:orientation="horizontal" |
|
||||
android:padding="4dp"> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatImageView |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:padding="4dp" |
|
||||
app:srcCompat="@drawable/ic_outline_views_24" |
|
||||
app:tint="?android:textColorPrimary" /> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/tvVideoViews" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="match_parent" |
|
||||
android:gravity="center_vertical" |
|
||||
android:textAppearance="?attr/textAppearanceButton" |
|
||||
tools:text="690000" /> |
|
||||
</LinearLayout> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/tvPostDate" |
|
||||
android:layout_width="0dp" |
|
||||
android:layout_height="match_parent" |
|
||||
android:layout_weight="1" |
|
||||
android:gravity="center_vertical" |
|
||||
android:maxLines="1" |
|
||||
android:padding="8dp" |
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption" |
|
||||
tools:text="2020-01-01 12:00:00" /> |
|
||||
|
|
||||
<!--<androidx.appcompat.widget.AppCompatImageView--> |
|
||||
<!-- android:id="@+id/btnMute"--> |
|
||||
<!-- android:layout_width="wrap_content"--> |
|
||||
<!-- android:layout_height="match_parent"--> |
|
||||
<!-- android:background="?selectableItemBackgroundBorderless"--> |
|
||||
<!-- android:padding="4dp"--> |
|
||||
<!-- android:visibility="gone"--> |
|
||||
<!-- app:srcCompat="@drawable/ic_volume_up_24"--> |
|
||||
<!-- app:tint="?android:textColorPrimary"--> |
|
||||
<!-- tools:visibility="visible" />--> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatImageView |
|
||||
android:id="@+id/btnDownload" |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="match_parent" |
|
||||
android:background="?selectableItemBackgroundBorderless" |
|
||||
android:padding="4dp" |
|
||||
app:srcCompat="@drawable/ic_download" |
|
||||
app:tint="?android:textColorPrimary" /> |
|
||||
</LinearLayout> |
|
||||
|
|
||||
<awais.instagrabber.customviews.RamboTextViewV2 |
|
||||
android:id="@+id/viewerCaption" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:background="?android:selectableItemBackground" |
|
||||
android:clickable="true" |
|
||||
android:clipToPadding="false" |
|
||||
android:ellipsize="end" |
|
||||
android:focusable="true" |
|
||||
android:maxLines="5" |
|
||||
android:paddingStart="8dp" |
|
||||
android:paddingLeft="8dp" |
|
||||
android:paddingEnd="8dp" |
|
||||
android:paddingRight="8dp" |
|
||||
android:paddingBottom="8dp" |
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1" |
|
||||
tools:text="Bottom text with hashtags etc." /> |
|
||||
</LinearLayout> |
|
@ -1,63 +1,86 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
xmlns:tools="http://schemas.android.com/tools" |
xmlns:tools="http://schemas.android.com/tools" |
||||
android:id="@+id/container" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:orientation="horizontal" |
|
||||
android:padding="8dp"> |
|
||||
|
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> |
||||
|
|
||||
<com.facebook.drawee.view.SimpleDraweeView |
|
||||
android:id="@+id/ivProfilePic" |
|
||||
android:layout_width="@dimen/profile_pic_size_regular" |
|
||||
android:layout_height="@dimen/profile_pic_size_regular" |
|
||||
android:background="?selectableItemBackgroundBorderless" |
|
||||
app:roundAsCircle="true" /> |
|
||||
|
<awais.instagrabber.customviews.ProfilePicView |
||||
|
android:id="@+id/profile_pic" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_margin="12dp" |
||||
|
android:transitionName="profile_pic" |
||||
|
app:layout_constraintBottom_toTopOf="@id/top_barrier" |
||||
|
app:layout_constraintEnd_toStartOf="@id/title" |
||||
|
app:layout_constraintHorizontal_bias="0" |
||||
|
app:layout_constraintHorizontal_chainStyle="packed" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:size="regular" /> |
||||
|
|
||||
<RelativeLayout |
|
||||
android:id="@+id/infoContainer" |
|
||||
|
<androidx.appcompat.widget.AppCompatTextView |
||||
|
android:id="@+id/title" |
||||
android:layout_width="0dp" |
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
|
||||
android:layout_weight="1" |
|
||||
android:animateLayoutChanges="true" |
|
||||
android:background="@null" |
|
||||
android:gravity="center" |
|
||||
android:orientation="vertical" |
|
||||
android:paddingStart="8dp" |
|
||||
android:paddingLeft="8dp" |
|
||||
android:paddingEnd="8dp" |
|
||||
android:paddingRight="8dp" |
|
||||
android:weightSum="2"> |
|
||||
|
android:layout_height="wrap_content" |
||||
|
android:ellipsize="marquee" |
||||
|
android:singleLine="true" |
||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" |
||||
|
app:layout_constrainedWidth="true" |
||||
|
app:layout_constraintBottom_toTopOf="@id/subtitle" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toEndOf="@id/profile_pic" |
||||
|
app:layout_constraintTop_toTopOf="@id/profile_pic" |
||||
|
tools:text="Username Username Username" /> |
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/title" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:gravity="center_vertical" |
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" |
|
||||
tools:text="username" /> |
|
||||
|
<androidx.appcompat.widget.AppCompatTextView |
||||
|
android:id="@+id/subtitle" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:ellipsize="end" |
||||
|
android:singleLine="true" |
||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" |
||||
|
app:layout_constraintBottom_toBottomOf="@id/profile_pic" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toStartOf="@id/title" |
||||
|
app:layout_constraintTop_toBottomOf="@id/title" |
||||
|
tools:text="Full name Full name Full name Full name Full name Full name Full name " |
||||
|
tools:visibility="gone" /> |
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/location" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:layout_below="@+id/title" |
|
||||
android:ellipsize="end" |
|
||||
android:gravity="center_vertical" |
|
||||
android:maxLines="1" |
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" |
|
||||
android:textSize="15sp" |
|
||||
android:visibility="visible" |
|
||||
tools:text="location" /> |
|
||||
</RelativeLayout> |
|
||||
|
<androidx.constraintlayout.widget.Barrier |
||||
|
android:id="@+id/top_barrier" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="0dp" |
||||
|
app:barrierAllowsGoneWidgets="true" |
||||
|
app:barrierDirection="bottom"/> |
||||
|
|
||||
<!--<androidx.appcompat.widget.AppCompatImageView--> |
|
||||
<!-- android:id="@+id/viewStoryPost"--> |
|
||||
<!-- android:layout_width="wrap_content"--> |
|
||||
<!-- android:layout_height="match_parent"--> |
|
||||
<!-- android:layout_gravity="center"--> |
|
||||
<!-- android:background="?selectableItemBackgroundBorderless"--> |
|
||||
<!-- app:srcCompat="@drawable/ic_open_in_new_24"--> |
|
||||
<!-- app:tint="?android:textColorPrimary" />--> |
|
||||
</LinearLayout> |
|
||||
|
<com.google.android.material.button.MaterialButton |
||||
|
android:id="@+id/location" |
||||
|
style="?borderlessButtonStyle" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="16dp" |
||||
|
android:layout_marginEnd="16dp" |
||||
|
android:elevation="0dp" |
||||
|
android:ellipsize="end" |
||||
|
android:insetTop="0dp" |
||||
|
android:insetBottom="0dp" |
||||
|
android:maxWidth="200dp" |
||||
|
android:maxLines="1" |
||||
|
android:minHeight="32dp" |
||||
|
android:paddingStart="8dp" |
||||
|
android:paddingEnd="8dp" |
||||
|
android:textAlignment="viewStart" |
||||
|
android:textAllCaps="false" |
||||
|
android:textColor="@android:color/white" |
||||
|
android:visibility="gone" |
||||
|
app:backgroundTint="@color/black_a50" |
||||
|
app:elevation="0dp" |
||||
|
app:icon="@drawable/ic_round_location_on_24" |
||||
|
app:iconSize="16dp" |
||||
|
app:iconTint="@color/white" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier" |
||||
|
app:rippleColor="@color/grey_600" |
||||
|
tools:text="Location, Location, Location, Location, " |
||||
|
tools:visibility="visible" /> |
||||
|
</merge> |
@ -1,19 +1,20 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/videoHolder" |
android:id="@+id/videoHolder" |
||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
|
||||
android:orientation="vertical"> |
|
||||
|
android:layout_height="wrap_content"> |
||||
|
|
||||
<include |
|
||||
android:id="@+id/item_feed_top" |
|
||||
layout="@layout/item_feed_top" /> |
|
||||
|
<include layout="@layout/item_feed_top" /> |
||||
|
|
||||
<include |
|
||||
android:id="@+id/video_post" |
|
||||
layout="@layout/layout_video_player_with_thumbnail" /> |
|
||||
|
<FrameLayout |
||||
|
android:id="@+id/post_container" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:layout_constraintBottom_toTopOf="@id/buttons_top_barrier" |
||||
|
app:layout_constraintTop_toBottomOf="@id/top_barrier" |
||||
|
tools:layout_height="100dp" /> |
||||
|
|
||||
<include |
|
||||
android:id="@+id/item_feed_bottom" |
|
||||
layout="@layout/item_feed_bottom" /> |
|
||||
</LinearLayout> |
|
||||
|
<include layout="@layout/layout_post_view_bottom" /> |
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -1,105 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<LinearLayout 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" |
|
||||
android:layout_marginTop="?attr/actionBarSize" |
|
||||
android:animateLayoutChanges="true" |
|
||||
android:orientation="vertical" |
|
||||
android:weightSum="3.2"> |
|
||||
|
|
||||
<include |
|
||||
android:id="@+id/topPanel" |
|
||||
layout="@layout/item_feed_top" /> |
|
||||
|
|
||||
<FrameLayout |
|
||||
android:id="@+id/container" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="0dp" |
|
||||
android:layout_weight="1.9"> |
|
||||
|
|
||||
<awais.instagrabber.customviews.helpers.NestedScrollableHost |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="match_parent"> |
|
||||
|
|
||||
<androidx.viewpager2.widget.ViewPager2 |
|
||||
android:id="@+id/mediaViewPager" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="match_parent" /> |
|
||||
</awais.instagrabber.customviews.helpers.NestedScrollableHost> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatTextView |
|
||||
android:id="@+id/mediaCounter" |
|
||||
android:layout_width="wrap_content" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:layout_gravity="center_horizontal|bottom" |
|
||||
android:background="@drawable/rounder_corner_semi_black_bg" |
|
||||
android:gravity="center" |
|
||||
android:padding="5dp" |
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption" |
|
||||
android:textColor="@android:color/white" |
|
||||
android:visibility="gone" /> |
|
||||
|
|
||||
<ProgressBar |
|
||||
android:id="@+id/progressView" |
|
||||
android:layout_width="48dp" |
|
||||
android:layout_height="48dp" |
|
||||
android:layout_gravity="center" |
|
||||
android:visibility="gone" /> |
|
||||
|
|
||||
<!-- Removing for now, will add in later versions --> |
|
||||
<!--<androidx.appcompat.widget.AppCompatImageView--> |
|
||||
<!-- android:id="@+id/ivToggleFullScreen"--> |
|
||||
<!-- android:layout_width="48dp"--> |
|
||||
<!-- android:layout_height="48dp"--> |
|
||||
<!-- android:layout_gravity="end|top"--> |
|
||||
<!-- android:background="?selectableItemBackgroundBorderless"--> |
|
||||
<!-- android:padding="4dp"--> |
|
||||
<!-- app:srcCompat="@drawable/ic_fullscreen"--> |
|
||||
<!-- app:tint="?android:textColorPrimary" />--> |
|
||||
</FrameLayout> |
|
||||
|
|
||||
<include |
|
||||
android:id="@+id/bottomPanel" |
|
||||
layout="@layout/item_feed_bottom" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="0dp" |
|
||||
android:layout_weight="1" /> |
|
||||
|
|
||||
<androidx.appcompat.widget.LinearLayoutCompat |
|
||||
android:id="@+id/postActions" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="0dp" |
|
||||
android:layout_weight="0.3" |
|
||||
android:background="#0000" |
|
||||
android:weightSum="2"> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatButton |
|
||||
android:id="@+id/btnLike" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:layout_marginStart="6dp" |
|
||||
android:layout_marginLeft="8dp" |
|
||||
android:layout_marginRight="8dp" |
|
||||
android:layout_weight="1" |
|
||||
android:textColor="@color/btn_lightpink_text_color" |
|
||||
android:textSize="18sp" |
|
||||
app:backgroundTint="@color/btn_lightpink_background" |
|
||||
tools:text="@string/like" /> |
|
||||
|
|
||||
<androidx.appcompat.widget.AppCompatButton |
|
||||
android:id="@+id/btnBookmark" |
|
||||
android:layout_width="match_parent" |
|
||||
android:layout_height="wrap_content" |
|
||||
android:layout_alignParentBottom="true" |
|
||||
android:layout_marginLeft="8dp" |
|
||||
android:layout_marginEnd="6dp" |
|
||||
android:layout_marginRight="8dp" |
|
||||
android:layout_weight="1" |
|
||||
android:text="@string/bookmark" |
|
||||
android:textColor="@color/btn_lightorange_text_color" |
|
||||
android:textSize="18sp" |
|
||||
app:backgroundTint="@color/btn_lightorange_background" /> |
|
||||
</androidx.appcompat.widget.LinearLayoutCompat> |
|
||||
</LinearLayout> |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue