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 |
|||
|
|||
import awais.instagrabber.models.FollowModel |
|||
|
|||
data class FriendshipListFetchResponse( |
|||
var nextMaxId: String?, |
|||
var status: String?, |
|||
var items: List<FollowModel>? |
|||
var users: List<User>? |
|||
) { |
|||
val isMoreAvailable: Boolean |
|||
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 |
|||
|
|||
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"?> |
|||
<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: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_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"?> |
|||
<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: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