From e4612a0dd02dbffc4dbdbe44279d202c12f4db5c Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 17:14:44 -0400 Subject: [PATCH 01/16] Rename .java to .kt --- .../{FollowViewerFragment.java => FollowViewerFragment.kt} | 0 .../viewmodels/{FollowViewModel.java => FollowViewModel.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/awais/instagrabber/fragments/{FollowViewerFragment.java => FollowViewerFragment.kt} (100%) rename app/src/main/java/awais/instagrabber/viewmodels/{FollowViewModel.java => FollowViewModel.kt} (100%) diff --git a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.kt similarity index 100% rename from app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java rename to app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.kt diff --git a/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt similarity index 100% rename from app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.java rename to app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt From 036edbea10efa6bae71c6ca9197140b7b2adfa8a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 17:14:45 -0400 Subject: [PATCH 02/16] greatly fix all the follower/ing stuff closes #1334 (assuming) closes #1340 (by implementing API search) closes #1342 closes #1472 closes #1473 --- .../instagrabber/adapters/FollowAdapter.java | 57 +- .../viewholder/FollowsViewHolder.java | 11 - .../fragments/FollowViewerFragment.kt | 655 ++++++------------ .../awais/instagrabber/models/FollowModel.kt | 49 -- .../repositories/FriendshipService.kt | 3 +- .../responses/FriendshipListFetchResponse.kt | 19 +- .../viewmodels/FollowViewModel.kt | 174 ++++- .../webservices/FriendshipRepository.kt | 45 +- .../expandableadapter/ExpandableGroup.java | 23 +- .../expandableadapter/ExpandableList.java | 10 +- .../res/layout/fragment_followers_viewer.xml | 1 - 11 files changed, 450 insertions(+), 597 deletions(-) delete mode 100644 app/src/main/java/awais/instagrabber/models/FollowModel.kt diff --git a/app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java index b11e0411..d8368aa5 100755 --- a/app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java @@ -12,12 +12,13 @@ import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import awais.instagrabber.R; import awais.instagrabber.adapters.viewholder.FollowsViewHolder; import awais.instagrabber.databinding.ItemFollowBinding; import awais.instagrabber.interfaces.OnGroupClickListener; -import awais.instagrabber.models.FollowModel; +import awais.instagrabber.repositories.responses.User; import awais.instagrabber.utils.TextUtils; import thoughtbot.expandableadapter.ExpandableGroup; import thoughtbot.expandableadapter.ExpandableList; @@ -27,28 +28,33 @@ import thoughtbot.expandableadapter.GroupViewHolder; // thanks to ThoughtBot's ExpandableRecyclerViewAdapter // https://github.com/thoughtbot/expandable-recycler-view public final class FollowAdapter extends RecyclerView.Adapter implements OnGroupClickListener, Filterable { + private final View.OnClickListener onClickListener; + private final ExpandableList expandableListOriginal; + private final boolean hasManyGroups; + private ExpandableList expandableList; + private final Filter filter = new Filter() { @Nullable @Override protected FilterResults performFiltering(final CharSequence filter) { - if (expandableList.groups != null) { - final boolean isFilterEmpty = TextUtils.isEmpty(filter); - final String query = isFilterEmpty ? null : filter.toString().toLowerCase(); - - for (int x = 0; x < expandableList.groups.size(); ++x) { - final ExpandableGroup expandableGroup = expandableList.groups.get(x); - final List items = expandableGroup.getItems(false); - final int itemCount = expandableGroup.getItemCount(false); - - for (int i = 0; i < itemCount; ++i) { - final FollowModel followModel = items.get(i); - - if (isFilterEmpty) followModel.setShown(true); - else followModel.setShown(hasKey(query, followModel.getUsername(), followModel.getFullName())); - } + final List filteredItems = new ArrayList(); + if (expandableListOriginal.groups == null || TextUtils.isEmpty(filter)) return null; + final String query = filter.toString().toLowerCase(); + final ArrayList groups = new ArrayList(); + for (int x = 0; x < expandableListOriginal.groups.size(); ++x) { + final ExpandableGroup expandableGroup = expandableListOriginal.groups.get(x); + final String title = expandableGroup.getTitle(); + final List items = expandableGroup.getItems(); + if (items != null) { + final List toReturn = items.stream() + .filter(u -> hasKey(query, u.getUsername(), u.getFullName())) + .collect(Collectors.toList()); + groups.add(new ExpandableGroup(title, toReturn)); } } - return null; + final FilterResults filterResults = new FilterResults(); + filterResults.values = new ExpandableList(groups, expandableList.expandedGroupIndexes); + return filterResults; } private boolean hasKey(final String key, final String username, final String name) { @@ -60,15 +66,20 @@ public final class FollowAdapter extends RecyclerView.Adapter groups) { - this.expandableList = new ExpandableList(groups); + this.expandableListOriginal = new ExpandableList(groups); + expandableList = this.expandableListOriginal; this.onClickListener = onClickListener; this.hasManyGroups = groups.size() > 1; } @@ -104,7 +115,7 @@ public final class FollowAdapter extends RecyclerView.Adapter = ArrayList() + private val followingModels: ArrayList = ArrayList() + private val followersModels: ArrayList = ArrayList() + private val allFollowing: ArrayList = ArrayList() + 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 - private final ArrayList followModels = new ArrayList<>(); - private final ArrayList followingModels = new ArrayList<>(); - private final ArrayList followersModels = new ArrayList<>(); - private final ArrayList 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 followingFetchCb = new ServiceCallback() { - @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 followersFetchCb = new ServiceCallback() { - @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); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + fragmentActivity = activity as AppCompatActivity + viewModel = ViewModelProvider(this).get(FollowViewModel::class.java) + setHasOptionsMenu(true) } - @NonNull - @Override - public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { if (root != null) { - shouldRefresh = false; - return root; + shouldRefresh = false + return root!! } - binding = FragmentFollowersViewerBinding.inflate(getLayoutInflater()); - root = binding.getRoot(); - return root; + binding = FragmentFollowersViewerBinding.inflate(layoutInflater) + root = binding.root + return root!! } - @Override - public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { - if (!shouldRefresh) return; - init(); - shouldRefresh = false; + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + 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"; + 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) } - setTitle(username); - resources = getResources(); - clickListener = v -> { - final Object tag = v.getTag(); - if (tag instanceof FollowModel) { - model = (FollowModel) tag; - final FollowViewerFragmentDirections.ActionFollowViewerFragmentToProfileFragment action = FollowViewerFragmentDirections - .actionFollowViewerFragmentToProfileFragment(); - action.setUsername("@" + model.getUsername()); - NavHostFragment.findNavController(this).navigate(action); + } + + 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) } - }; - 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 cb = new ServiceCallback() { - @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 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 - 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; + 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) } - cb.onSuccess(response); - }), Dispatchers.getIO()) - ); + } + 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 { - refreshAdapter(followModels, null, null, null); - layoutManager.scrollToPosition(0); + isCompare = true + listCompare() } + return true } - 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 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 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 followModels, - final ArrayList followingModels, - final ArrayList followersModels, - final ArrayList allFollowing) { - loading = false; - final ArrayList groups = new ArrayList<>(1); - + private fun refreshAdapter( + followModels: List?, + allFollowing: List?, + followingModels: List?, + followersModels: List? + ) { + val groups: ArrayList = 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)); + 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(new ExpandableGroup(type, followModels)); - } else return; - adapter = new FollowAdapter(clickListener, groups); - adapter.toggleGroup(0); - binding.rvFollow.setAdapter(adapter); + 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" } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/FollowModel.kt b/app/src/main/java/awais/instagrabber/models/FollowModel.kt deleted file mode 100644 index d71196e1..00000000 --- a/app/src/main/java/awais/instagrabber/models/FollowModel.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt b/app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt index 6e783dfa..7bff0c75 100644 --- a/app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt +++ b/app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt @@ -1,6 +1,7 @@ package awais.instagrabber.repositories import awais.instagrabber.repositories.responses.FriendshipChangeResponse +import awais.instagrabber.repositories.responses.FriendshipListFetchResponse import awais.instagrabber.repositories.responses.FriendshipRestrictResponse import retrofit2.http.* @@ -25,7 +26,7 @@ interface FriendshipService { @Path("userId") userId: Long, @Path("type") type: String, // following or followers @QueryMap(encoded = true) queryParams: Map, - ): String + ): FriendshipListFetchResponse @FormUrlEncoded @POST("/api/v1/friendships/{action}/") diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt b/app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt index a9ff7c9b..25145aa6 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt @@ -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? + var users: List? ) { 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): FriendshipListFetchResponse { - this.items = items - return this - } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt index dcd65d4a..f2a0a428 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt @@ -1,19 +1,167 @@ -package awais.instagrabber.viewmodels; +package awais.instagrabber.viewmodels -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; +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 -import java.util.List; +class FollowViewModel : ViewModel() { + // data + val userId = MutableLiveData() + private val followers = MutableLiveData>() + private val followings = MutableLiveData>() + private val searchResults = MutableLiveData>() -import awais.instagrabber.models.FollowModel; + // cursors + private val followersMaxId = MutableLiveData("") + private val followingMaxId = MutableLiveData("") + private val searchingMaxId = MutableLiveData("") + private val searchQuery = MutableLiveData() -public class FollowViewModel extends ViewModel { - private MutableLiveData> list; - - public MutableLiveData> getList() { - if (list == null) { - list = new MutableLiveData<>(); + // comparison + val status: LiveData> = object : MediatorLiveData>() { + 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) + } + } } - return list; + val comparison: LiveData, List, List>> = + object : MediatorLiveData, List, List>>() { + init { + addSource(status) { + if (it.first && it.second) { + val followersList = followers.value!! + val followingList = followings.value!! + val allUsers: MutableList = mutableListOf() + allUsers.addAll(followersList) + allUsers.addAll(followingList) + val followersMap = followersList.groupBy { it.pk } + val followingMap = followingList.groupBy { it.pk } + val mutual: MutableList = mutableListOf() + val onlyFollowing: MutableList = mutableListOf() + val onlyFollowers: MutableList = 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> { + val data = MutableLiveData>() + 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> { + return if (follower) followers else followings + } + + fun search(follower: Boolean): LiveData> { + val data = MutableLiveData>() + 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> { + 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() + followers.value = listOf() + searchResults.value = listOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt b/app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt index d286ed09..7736399f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt +++ b/app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt @@ -1,15 +1,11 @@ package awais.instagrabber.webservices -import awais.instagrabber.models.FollowModel import awais.instagrabber.repositories.FriendshipService import awais.instagrabber.repositories.responses.FriendshipChangeResponse import awais.instagrabber.repositories.responses.FriendshipListFetchResponse import awais.instagrabber.repositories.responses.FriendshipRestrictResponse import awais.instagrabber.utils.Utils import awais.instagrabber.webservices.RetrofitFactory.retrofit -import org.json.JSONArray -import org.json.JSONException -import org.json.JSONObject class FriendshipRepository(private val service: FriendshipService) { @@ -113,43 +109,12 @@ class FriendshipRepository(private val service: FriendshipService) { follower: Boolean, targetUserId: Long, maxId: String?, + query: String? ): FriendshipListFetchResponse { - val queryMap = if (maxId != null) mapOf("max_id" to maxId) else emptyMap() - val response = service.getList(targetUserId, if (follower) "followers" else "following", queryMap) - return parseListResponse(response) - } - - @Throws(JSONException::class) - private fun parseListResponse(body: String): FriendshipListFetchResponse { - val root = JSONObject(body) - val nextMaxId = root.optString("next_max_id") - val status = root.optString("status") - val itemsJson = root.optJSONArray("users") - val items = parseItems(itemsJson) - return FriendshipListFetchResponse( - nextMaxId, - status, - items - ) - } - - @Throws(JSONException::class) - private fun parseItems(items: JSONArray?): List { - if (items == null) { - return emptyList() - } - val followModels = mutableListOf() - for (i in 0 until items.length()) { - val itemJson = items.optJSONObject(i) ?: continue - val followModel = FollowModel( - itemJson.getString("pk"), - itemJson.getString("username"), - itemJson.optString("full_name"), - itemJson.getString("profile_pic_url") - ) - followModels.add(followModel) - } - return followModels + val queryMap: MutableMap = mutableMapOf() + if (!maxId.isNullOrEmpty()) queryMap.set("max_id", maxId) + if (!query.isNullOrEmpty()) queryMap.set("query", query) + return service.getList(targetUserId, if (follower) "followers" else "following", queryMap.toMap()) } companion object { diff --git a/app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java b/app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java index 0e499ba8..f1163550 100755 --- a/app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java +++ b/app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java @@ -3,13 +3,13 @@ package thoughtbot.expandableadapter; import java.util.ArrayList; import java.util.List; -import awais.instagrabber.models.FollowModel; +import awais.instagrabber.repositories.responses.User; public class ExpandableGroup { private final String title; - private final List items; + private final List items; - public ExpandableGroup(final String title, final List items) { + public ExpandableGroup(final String title, final List items) { this.title = title; this.items = items; } @@ -18,22 +18,13 @@ public class ExpandableGroup { return title; } - public List getItems(final boolean filtered) { - if (!filtered) return items; - final ArrayList followModels = new ArrayList<>(); - for (final FollowModel followModel : items) if (followModel.isShown()) followModels.add(followModel); - return followModels; + public List getItems() { + return items; } - public int getItemCount(final boolean filtered) { + public int getItemCount() { if (items != null) { - final int size = items.size(); - if (filtered) { - int finalSize = 0; - for (int i = 0; i < size; ++i) if (items.get(i).isShown()) ++finalSize; - return finalSize; - } - return size; + return items.size(); } return 0; } diff --git a/app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java b/app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java index 408e46cf..5006fb67 100755 --- a/app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java +++ b/app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java @@ -1,6 +1,7 @@ package thoughtbot.expandableadapter; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; @@ -15,6 +16,13 @@ public final class ExpandableList { this.expandedGroupIndexes = new boolean[groupsSize]; } + public ExpandableList(@NonNull final ArrayList groups, + @Nullable final boolean[] expandedGroupIndexes) { + this.groups = groups; + this.groupsSize = groups.size(); + this.expandedGroupIndexes = expandedGroupIndexes; + } + public int getVisibleItemCount() { int count = 0; for (int i = 0; i < groupsSize; i++) count = count + numberOfVisibleItemsInGroup(i); @@ -36,7 +44,7 @@ public final class ExpandableList { } private int numberOfVisibleItemsInGroup(final int group) { - return expandedGroupIndexes[group] ? groups.get(group).getItemCount(true) + 1 : 1; + return expandedGroupIndexes[group] ? groups.get(group).getItemCount() + 1 : 1; } public int getFlattenedGroupIndex(@NonNull final ExpandableListPosition listPosition) { diff --git a/app/src/main/res/layout/fragment_followers_viewer.xml b/app/src/main/res/layout/fragment_followers_viewer.xml index 3d5a21ae..6452c078 100644 --- a/app/src/main/res/layout/fragment_followers_viewer.xml +++ b/app/src/main/res/layout/fragment_followers_viewer.xml @@ -16,6 +16,5 @@ android:paddingLeft="8dp" android:paddingEnd="8dp" android:paddingRight="8dp" - app:layoutManager="LinearLayoutManager" tools:listitem="@layout/item_follow" /> \ No newline at end of file From b135276fa28669fdd8c28152f351ddb686655328 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 17:22:02 -0400 Subject: [PATCH 03/16] try-catch for anonymous user parsing --- .../viewmodels/ProfileFragmentViewModel.kt | 2 +- .../webservices/GraphQLRepository.kt | 94 ++++++++++--------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt index 3acd3e66..5cc85b29 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt @@ -222,7 +222,7 @@ class ProfileFragmentViewModel( private suspend fun fetchUser( currentUser: User?, stateUsername: String, - ): User { + ): User? { if (currentUser != null) { // logged in val tempUser = userRepository.getUsernameInfo(stateUsername) diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt b/app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt index c151bd31..e54b6ec2 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt @@ -178,51 +178,59 @@ open class GraphQLRepository(private val service: GraphQLService) { // TODO convert string response to a response class open suspend fun fetchUser( username: String, - ): User { + ): User? { val response = service.getUser(username) - val body = JSONObject(response - .split("").get(0) - .trim().replace(Regex("\\};$"), "}")) - val userJson = body - .getJSONObject("entry_data") - .getJSONArray("ProfilePage") - .getJSONObject(0) - .getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_USER) - val isPrivate = userJson.getBoolean("is_private") - val id = userJson.optLong(Constants.EXTRAS_ID, 0) - val timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media") - // if (timelineMedia.has("edges")) { - // final JSONArray edges = timelineMedia.getJSONArray("edges"); - // } - var url: String? = userJson.optString("external_url") - if (url.isNullOrBlank()) url = null - return User( - id, - username, - userJson.getString("full_name"), - isPrivate, - userJson.getString("profile_pic_url_hd"), - userJson.getBoolean("is_verified"), - friendshipStatus = FriendshipStatus( - userJson.optBoolean("followed_by_viewer"), - userJson.optBoolean("follows_viewer"), - userJson.optBoolean("blocked_by_viewer"), - false, + try { + val body = JSONObject( + response + .split("").get(0) + .trim().replace(Regex("\\};$"), "}") + ) + val userJson = body + .getJSONObject("entry_data") + .getJSONArray("ProfilePage") + .getJSONObject(0) + .getJSONObject("graphql") + .getJSONObject(Constants.EXTRAS_USER) + val isPrivate = userJson.getBoolean("is_private") + val id = userJson.optLong(Constants.EXTRAS_ID, 0) + val timelineMedia = userJson.getJSONObject("edge_owner_to_timeline_media") + // if (timelineMedia.has("edges")) { + // final JSONArray edges = timelineMedia.getJSONArray("edges"); + // } + var url: String? = userJson.optString("external_url") + if (url.isNullOrBlank()) url = null + return User( + id, + username, + userJson.getString("full_name"), isPrivate, - userJson.optBoolean("has_requested_viewer"), - userJson.optBoolean("requested_by_viewer"), - false, - userJson.optBoolean("restricted_by_viewer"), - false - ), - mediaCount = timelineMedia.getLong("count"), - followerCount = userJson.getJSONObject("edge_followed_by").getLong("count"), - followingCount = userJson.getJSONObject("edge_follow").getLong("count"), - biography = userJson.getString("biography"), - externalUrl = url, - ) + userJson.getString("profile_pic_url_hd"), + userJson.getBoolean("is_verified"), + friendshipStatus = FriendshipStatus( + userJson.optBoolean("followed_by_viewer"), + userJson.optBoolean("follows_viewer"), + userJson.optBoolean("blocked_by_viewer"), + false, + isPrivate, + userJson.optBoolean("has_requested_viewer"), + userJson.optBoolean("requested_by_viewer"), + false, + userJson.optBoolean("restricted_by_viewer"), + false + ), + mediaCount = timelineMedia.getLong("count"), + followerCount = userJson.getJSONObject("edge_followed_by").getLong("count"), + followingCount = userJson.getJSONObject("edge_follow").getLong("count"), + biography = userJson.getString("biography"), + externalUrl = url, + ) + } + catch (e: Exception) { + Log.e(TAG, "fetchUser failed", e) + return null + } } // TODO convert string response to a response class From 40907a8bc118c7ac381ad0c2f840ee1f8b5da85a Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 17:23:58 -0400 Subject: [PATCH 04/16] close #1522 --- .../fragments/settings/DownloadsPreferencesFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java index d79974c5..1ff802fd 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java @@ -94,7 +94,7 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment { Utils.setupSelectedDir(context, data); String path; try { - path = URLDecoder.decode(data.getData().toString(), StandardCharsets.UTF_8.toString()); + path = URLDecoder.decode(data.getData().toString(), StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { path = data.getData().toString(); } From 7d1dca07c1666de0d11bc3590aba694396a555cd Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 17:28:05 -0400 Subject: [PATCH 05/16] close #1528 --- .../java/awais/instagrabber/adapters/DirectItemsAdapter.java | 2 +- .../java/awais/instagrabber/models/enums/DirectItemType.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java index 165eddc2..dd744917 100644 --- a/app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java @@ -139,7 +139,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter = mutableMapOf() @JvmStatic - fun getId(id: Int): DirectItemType? { - return map[id] + fun getTypeFromId(id: Int): DirectItemType { + return map[id] ?: UNKNOWN } fun getName(directItemType: DirectItemType): String? { From b1400e8e6fd7c8f703259ec5c2f986387076dbdf Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 7 Jul 2021 19:51:13 -0400 Subject: [PATCH 06/16] change ratelimited info link https://twitter.com/aldonogueira/status/1411390944595828738 --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d934a96..9c601e7f 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -486,7 +486,7 @@ Barinsta Crash Report Select an email app to send crash logs Not found! - Your IP has been rate limited by Instagram. Wait for an hour and try again. <a href=\"https://redd.it/msxlko\">Learn more.</a> + Your IP has been rate limited by Instagram. <a href=\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\">Learn more.</a> Skip this update You\'re already on the latest version Screen order From 585db4056acda24caeaceb970cad691c8f1842d6 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 8 Jul 2021 16:51:17 -0400 Subject: [PATCH 07/16] fix following request not refreshing (untested) --- .../awais/instagrabber/adapters/NotificationsAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java index 9a9748e2..55fd2b19 100644 --- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java @@ -24,12 +24,12 @@ public final class NotificationsAdapter extends ListAdapter DIFF_CALLBACK = new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(final Notification oldItem, final Notification newItem) { - return oldItem != null && newItem != null && oldItem.getPk().equals(newItem.getPk()); + return oldItem.getPk().equals(newItem.getPk()); } @Override public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) { - return oldItem.getPk().equals(newItem.getPk()); + return oldItem.getPk().equals(newItem.getPk()) && oldItem.getType() == newItem.getType(); } }; From c82080acf42e6a33abc78579029c126faa5a6e95 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 8 Jul 2021 16:51:46 -0400 Subject: [PATCH 08/16] restore marking feed stories as seen --- .../fragments/StoryViewerFragment.kt | 168 +++++++++--------- .../fragments/main/FeedFragment.java | 6 +- .../repositories/responses/stories/Story.kt | 2 +- .../viewmodels/StoryFragmentViewModel.kt | 38 +++- 4 files changed, 116 insertions(+), 98 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt index 40362e0c..897f4746 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt @@ -17,10 +17,9 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.widget.PopupMenu import androidx.core.view.GestureDetectorCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController @@ -31,7 +30,6 @@ import awais.instagrabber.R import awais.instagrabber.adapters.StoriesAdapter import awais.instagrabber.customviews.helpers.SwipeGestureListener import awais.instagrabber.databinding.FragmentStoryViewerBinding -import awais.instagrabber.fragments.main.ProfileFragment import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.interfaces.SwipeEvent import awais.instagrabber.models.Resource @@ -41,9 +39,7 @@ import awais.instagrabber.models.enums.StoryPaginationType import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.stories.* -import awais.instagrabber.utils.Constants import awais.instagrabber.utils.DownloadUtils.download -import awais.instagrabber.utils.TextUtils.epochSecondToString import awais.instagrabber.utils.ResponseBodyUtils import awais.instagrabber.utils.Utils import awais.instagrabber.utils.extensions.TAG @@ -79,7 +75,6 @@ class StoryViewerFragment : Fragment() { private var gestureDetector: GestureDetectorCompat? = null private val storiesRepository: StoriesRepository? = null private val mediaRepository: MediaRepository? = null - private var live: Broadcast? = null private var menuProfile: MenuItem? = null private var profileVisible: Boolean = false private var player: SimpleExoPlayer? = null @@ -218,8 +213,8 @@ class StoryViewerFragment : Fragment() { storiesViewModel.setMedia(position) } binding.storiesList.adapter = storiesAdapter - storiesViewModel.getCurrentStory().observe(fragmentActivity, { - if (it?.items != null) { + storiesViewModel.getCurrentStory().observe(viewLifecycleOwner, { + if (it?.items != null && it.items.size > 1) { val storyMedias = it.items.toMutableList() val newItem = storyMedias.get(0) newItem.isCurrentSlide = true @@ -231,23 +226,24 @@ class StoryViewerFragment : Fragment() { else View.GONE } else { + if (it?.items != null) storiesViewModel.setMedia(0) binding.listToggle.isEnabled = false binding.storiesList.visibility = View.GONE } }) - storiesViewModel.getDate().observe(fragmentActivity, { + storiesViewModel.getDate().observe(viewLifecycleOwner, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.subtitle = it }) - storiesViewModel.getTitle().observe(fragmentActivity, { + storiesViewModel.getTitle().observe(viewLifecycleOwner, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.title = it }) - storiesViewModel.getCurrentMedia().observe(fragmentActivity, { refreshStory(it) }) - storiesViewModel.getCurrentIndex().observe(fragmentActivity, { + storiesViewModel.getCurrentMedia().observe(viewLifecycleOwner, { refreshStory(it) }) + storiesViewModel.getCurrentIndex().observe(viewLifecycleOwner, { storiesAdapter!!.paginate(it) }) - storiesViewModel.getOptions().observe(fragmentActivity, { + storiesViewModel.getOptions().observe(viewLifecycleOwner, { binding.stickers.isEnabled = it.first.size > 0 }) } @@ -267,65 +263,28 @@ class StoryViewerFragment : Fragment() { @SuppressLint("ClickableViewAccessibility") private fun setupListeners() { - var liveModels: LiveData?>? = null if (currentFeedStoryIndex >= 0) { val type = options!!.type when (type) { StoryViewerOptions.Type.HIGHLIGHT -> { storiesViewModel.fetchHighlights(options!!.id) - liveModels = storiesViewModel.getHighlights() + storiesViewModel.highlights.observe(viewLifecycleOwner) { + setupMultipage(it) + } } StoryViewerOptions.Type.FEED_STORY_POSITION -> { val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? - liveModels = feedStoriesViewModel!!.list + setupMultipage(feedStoriesViewModel!!.list.value) } StoryViewerOptions.Type.STORY_ARCHIVE -> { val archivesViewModel = listViewModel as ArchivesViewModel? - liveModels = archivesViewModel!!.list + setupMultipage(archivesViewModel!!.list.value) } StoryViewerOptions.Type.USER -> { resetView() } } } - if (liveModels != null) liveModels.observe(viewLifecycleOwner, { models -> - storiesViewModel.getPagination().observe(fragmentActivity, { - if (models != null) { - when (it) { - StoryPaginationType.FORWARD -> { - if (currentFeedStoryIndex == models.size - 1) - Toast.makeText( - context, - R.string.no_more_stories, - Toast.LENGTH_SHORT - ).show() - else paginateStories(false, currentFeedStoryIndex == models.size - 2) - } - StoryPaginationType.BACKWARD -> { - if (currentFeedStoryIndex == 0) - Toast.makeText( - context, - R.string.no_more_stories, - Toast.LENGTH_SHORT - ).show() - else paginateStories(true, false) - } - StoryPaginationType.ERROR -> { - Toast.makeText( - context, - R.string.downloader_unknown_error, - Toast.LENGTH_SHORT - ).show() - } - } - } - }) - if (models != null && !models.isEmpty()) { - binding.btnBackward.isEnabled = currentFeedStoryIndex != 0 - binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1 - resetView() - } - }) val context = context ?: return swipeEvent = SwipeEvent { isRightSwipe: Boolean -> @@ -358,9 +317,46 @@ class StoryViewerFragment : Fragment() { binding.imageViewer.setTapListener(simpleOnGestureListener) } + private fun setupMultipage(models: List?) { + if (models == null) return + storiesViewModel.getPagination().observe(viewLifecycleOwner, { + when (it) { + StoryPaginationType.FORWARD -> { + if (currentFeedStoryIndex == models.size - 1) + Toast.makeText( + context, + R.string.no_more_stories, + Toast.LENGTH_SHORT + ).show() + else paginateStories(false, currentFeedStoryIndex == models.size - 2) + } + StoryPaginationType.BACKWARD -> { + if (currentFeedStoryIndex == 0) + Toast.makeText( + context, + R.string.no_more_stories, + Toast.LENGTH_SHORT + ).show() + else paginateStories(true, false) + } + StoryPaginationType.ERROR -> { + Toast.makeText( + context, + R.string.downloader_unknown_error, + Toast.LENGTH_SHORT + ).show() + } + } + }) + if (!models.isEmpty()) { + binding.btnBackward.isEnabled = currentFeedStoryIndex != 0 + binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1 + resetView() + } + } + private fun resetView() { val context = context ?: return - live = null if (menuProfile != null) menuProfile!!.isVisible = false binding.imageViewer.controller = null releasePlayer() @@ -368,7 +364,7 @@ class StoryViewerFragment : Fragment() { var fetchOptions: StoryViewerOptions? = null when (type) { StoryViewerOptions.Type.HIGHLIGHT -> { - val models = storiesViewModel.getHighlights().value + val models = storiesViewModel.highlights.value if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) { Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show() return @@ -379,10 +375,15 @@ class StoryViewerFragment : Fragment() { val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? val models = feedStoriesViewModel!!.list.value if (models == null || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) return - val (_, _, _, _, user, _, _, _, _, _, _, broadcast) = models[currentFeedStoryIndex] - currentStoryUsername = user!!.username - fetchOptions = StoryViewerOptions.forUser(user.pk, currentStoryUsername) - live = broadcast + val userStory = models[currentFeedStoryIndex] + currentStoryUsername = userStory.user!!.username + fetchOptions = StoryViewerOptions.forUser(userStory.user.pk, currentStoryUsername) + val live = userStory.broadcast + if (live != null) { + storiesViewModel.setStory(userStory) + refreshLive(live) + return + } } StoryViewerOptions.Type.STORY_ARCHIVE -> { val archivesViewModel = listViewModel as ArchivesViewModel? @@ -405,11 +406,7 @@ class StoryViewerFragment : Fragment() { storiesViewModel.fetchSingleMedia(options!!.id) return } - if (live != null) { - refreshLive() - return - } - storiesViewModel.fetchStory(fetchOptions).observe(fragmentActivity, { + storiesViewModel.fetchStory(fetchOptions).observe(viewLifecycleOwner, { if (it.status == Resource.Status.ERROR) { Toast.makeText(context, "Error: " + it.message, Toast.LENGTH_SHORT).show() } @@ -417,18 +414,14 @@ class StoryViewerFragment : Fragment() { } @Synchronized - private fun refreshLive() { + private fun refreshLive(live: Broadcast) { + binding.btnDownload.isEnabled = false + binding.stickers.isEnabled = false + binding.listToggle.isEnabled = false + binding.btnShare.isEnabled = false + binding.btnReply.isEnabled = false releasePlayer() - setupLive(live!!.dashPlaybackUrl ?: live!!.dashAbrPlaybackUrl ?: return) - val actionBar = fragmentActivity.supportActionBar - actionBarSubtitle = epochSecondToString(live!!.publishedTime!!) - if (actionBar != null) { - try { - actionBar.setSubtitle(actionBarSubtitle) - } catch (e: Exception) { - Log.e(TAG, "refreshLive: ", e) - } - } + setupLive(live.dashPlaybackUrl ?: live.dashAbrPlaybackUrl ?: return) } @Synchronized @@ -447,14 +440,19 @@ class StoryViewerFragment : Fragment() { binding.btnReply.isEnabled = currentStory.canReply if (itemType === MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(url) else setupImage(url) -// if (Utils.settingsHelper.getBoolean(MARK_AS_SEEN)) storiesRepository!!.seen( -// csrfToken, -// userId, -// deviceId, -// currentStory!!.id!!, -// currentStory!!.takenAt, -// System.currentTimeMillis() / 1000 -// ) + if (options!!.type == StoryViewerOptions.Type.FEED_STORY_POSITION + && Utils.settingsHelper.getBoolean(PreferenceKeys.MARK_AS_SEEN)) { + val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? + storiesViewModel.markAsSeen(currentStory).observe(viewLifecycleOwner) { m -> + if (m.status == Resource.Status.SUCCESS && m.data != null) { + val liveModels: MutableLiveData> = feedStoriesViewModel!!.list + val models = liveModels.value + val modelsCopy: MutableList = models!!.toMutableList() + modelsCopy.set(currentFeedStoryIndex, m.data) + liveModels.postValue(modelsCopy) + } + } + } } private fun downloadStory() { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index 593ea96f..c7f6d0b7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -322,7 +322,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onResume() { super.onResume(); - binding.getRoot().postDelayed(feedStoriesAdapter::notifyDataSetChanged, 1000); + // temporary fix + feedStoriesViewModel.getList().removeObservers(getViewLifecycleOwner()); + feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList); } @Override @@ -398,8 +400,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre storiesFetching = false; //noinspection unchecked feedStoriesViewModel.getList().postValue((List) feedStoryModels); - //noinspection unchecked - feedStoriesAdapter.submitList((List) feedStoryModels); if (storyListMenu != null) storyListMenu.setVisible(true); updateSwipeRefreshState(); }), Dispatchers.getIO()) diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt index b8288127..eb75944f 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt @@ -11,7 +11,7 @@ data class Story( val latestReelMedia: Long? = null, // = timestamp val mediaCount: Int? = null, // for stories and highlights - var seen: Long? = null, + val seen: Long? = null, val user: User? = null, // for stories val muted: Boolean? = null, diff --git a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt index d463c251..8979705e 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt @@ -60,21 +60,22 @@ class StoryFragmentViewModel : ViewModel() { private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() } // for highlights ONLY - private val highlights = MutableLiveData?>() + val highlights = MutableLiveData?>() /* set functions */ fun setStory(story: Story) { - if (story.items == null || story.items.size == 0) { - pagination.postValue(StoryPaginationType.ERROR) - return - } currentStory.postValue(story) storyTitle.postValue(story.title ?: story.user?.username) if (story.broadcast != null) { date.postValue(story.dateTime) type.postValue(MediaItemType.MEDIA_TYPE_LIVE) pagination.postValue(StoryPaginationType.DO_NOTHING) + return + } + if (story.items == null || story.items.size == 0) { + pagination.postValue(StoryPaginationType.ERROR) + return } } @@ -184,10 +185,6 @@ class StoryFragmentViewModel : ViewModel() { /* get functions */ - fun getHighlights(): LiveData?> { - return highlights - } - fun getCurrentStory(): LiveData { return currentStory } @@ -472,4 +469,27 @@ class StoryFragmentViewModel : ViewModel() { } return data } + + fun markAsSeen(storyMedia: StoryMedia): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + viewModelScope.launch(Dispatchers.IO) { + try { + storiesRepository.seen( + csrfToken!!, + userId, + deviceId, + storyMedia.id, + storyMedia.takenAt, + System.currentTimeMillis() / 1000 + ) + val oldStory = currentStory.value!! + val newStory = oldStory.copy(seen = storyMedia.takenAt) + data.postValue(success(newStory)) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + } + } + return data + } } \ No newline at end of file From 525cf379073076486ca5e1c91f539e683ec6b853 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 8 Jul 2021 20:40:59 -0400 Subject: [PATCH 09/16] do not mark as seen more than once --- .../awais/instagrabber/viewmodels/StoryFragmentViewModel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt index 8979705e..00717239 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt @@ -473,7 +473,9 @@ class StoryFragmentViewModel : ViewModel() { fun markAsSeen(storyMedia: StoryMedia): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - viewModelScope.launch(Dispatchers.IO) { + val oldStory = currentStory.value!! + if (oldStory.seen != null && oldStory.seen >= storyMedia.takenAt) data.postValue(success(null)) + else viewModelScope.launch(Dispatchers.IO) { try { storiesRepository.seen( csrfToken!!, @@ -483,7 +485,6 @@ class StoryFragmentViewModel : ViewModel() { storyMedia.takenAt, System.currentTimeMillis() / 1000 ) - val oldStory = currentStory.value!! val newStory = oldStory.copy(seen = storyMedia.takenAt) data.postValue(success(newStory)) } catch (e: Exception) { From 4cfb8e6a41474255ea81f444038447ae1669e598 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 8 Jul 2021 20:42:46 -0400 Subject: [PATCH 10/16] fix reel_share layout (date/check location) --- .../awais/instagrabber/customviews/ChatMessageLayout.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java b/app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java index 50d8a54d..7d09d929 100644 --- a/app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java +++ b/app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java @@ -94,7 +94,7 @@ public class ChatMessageLayout extends FrameLayout { heightSize += viewPartMainHeight; } else if (firstChildId == R.id.raven_media_container || firstChildId == R.id.profile_container || firstChildId == R.id.voice_media || firstChildId == R.id.story_container || firstChildId == R.id.media_share_container || firstChildId == R.id.link_container - || firstChildId == R.id.ivAnimatedMessage) { + || firstChildId == R.id.ivAnimatedMessage || firstChildId == R.id.reel_share_container) { widthSize += viewPartMainWidth; heightSize += viewPartMainHeight + viewPartInfoHeight; } else { @@ -104,12 +104,6 @@ public class ChatMessageLayout extends FrameLayout { if (firstChild instanceof TextView) { textMessage = (TextView) firstChild; } - else if (firstChildId == R.id.reel_share_container) { - textMessage = (TextView) ((ConstraintLayout) firstChild).getChildAt(5); - } - else if (firstChildId == R.id.story_container) { - textMessage = (TextView) ((ConstraintLayout) firstChild).getChildAt(2); - } else textMessage = null; if (textMessage != null) { viewPartMainLineCount = textMessage.getLineCount(); From ce764ec82f97d176124463d184f8bca9412a826c Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 14:54:00 -0400 Subject: [PATCH 11/16] fix #1582, probably not tested because i don't have this issue --- .../fragments/CollectionPostsFragment.java | 5 ++++- .../instagrabber/fragments/HashTagFragment.java | 12 +++++++----- .../instagrabber/fragments/LocationFragment.java | 12 +++++++----- .../instagrabber/fragments/SavedViewerFragment.java | 5 ++++- .../instagrabber/fragments/TopicPostsFragment.java | 5 ++++- .../instagrabber/fragments/main/FeedFragment.java | 4 +++- .../instagrabber/fragments/main/ProfileFragment.kt | 6 +++++- 7 files changed, 34 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java index f69ebcc9..533a116f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java @@ -56,6 +56,7 @@ import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.saved.SavedCollection; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; @@ -450,7 +451,9 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay } private void updateSwipeRefreshState() { - binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) + ); } private void navigateToProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 189820f6..f1c08306 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -67,7 +67,7 @@ import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import awais.instagrabber.webservices.GraphQLRepository; import awais.instagrabber.webservices.ServiceCallback; -import awais.instagrabber.webservices.StoriesRepository; +//import awais.instagrabber.webservices.StoriesRepository; import awais.instagrabber.webservices.TagsService; import kotlinx.coroutines.Dispatchers; @@ -87,11 +87,11 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private String hashtag; private Hashtag hashtagModel = null; private ActionMode actionMode; - private StoriesRepository storiesRepository; +// private StoriesRepository storiesRepository; private boolean isLoggedIn; private TagsService tagsService; private GraphQLRepository graphQLRepository; - private boolean storiesFetching; +// private boolean storiesFetching; private Set selectedFeedModels; private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_HASHTAG_POSTS_LAYOUT); private LayoutHashtagDetailsBinding hashtagDetailsBinding; @@ -280,7 +280,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; tagsService = isLoggedIn ? TagsService.getInstance() : null; - storiesRepository = isLoggedIn ? StoriesRepository.Companion.getInstance() : null; +// storiesRepository = isLoggedIn ? StoriesRepository.Companion.getInstance() : null; graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance(); setHasOptionsMenu(true); } @@ -578,7 +578,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } private void updateSwipeRefreshState() { - binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) + ); } private void navigateToProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 17f1f1b9..17aad8aa 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -64,7 +64,7 @@ import awais.instagrabber.utils.Utils; import awais.instagrabber.webservices.GraphQLRepository; import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.ServiceCallback; -import awais.instagrabber.webservices.StoriesRepository; +//import awais.instagrabber.webservices.StoriesRepository; import kotlinx.coroutines.Dispatchers; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -81,11 +81,11 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private long locationId; private Location locationModel; private ActionMode actionMode; - private StoriesRepository storiesRepository; +// private StoriesRepository storiesRepository; private GraphQLRepository graphQLRepository; private LocationService locationService; private boolean isLoggedIn; - private boolean storiesFetching; +// private boolean storiesFetching; private Set selectedFeedModels; private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_LOCATION_POSTS_LAYOUT); private LayoutLocationDetailsBinding locationDetailsBinding; @@ -274,7 +274,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; locationService = isLoggedIn ? LocationService.getInstance() : null; - storiesRepository = StoriesRepository.Companion.getInstance(); +// storiesRepository = StoriesRepository.Companion.getInstance(); graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance(); setHasOptionsMenu(true); } @@ -578,7 +578,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } private void updateSwipeRefreshState() { - binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) + ); } private void navigateToProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java index 57c40b85..c7f59fcf 100644 --- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java @@ -39,6 +39,7 @@ import awais.instagrabber.fragments.main.ProfileFragmentDirections; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.repositories.responses.Media; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; @@ -313,7 +314,9 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL } private void updateSwipeRefreshState() { - binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) + ); } private void navigateToProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java index acbc2eef..1d05ddca 100644 --- a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java @@ -53,6 +53,7 @@ import awais.instagrabber.fragments.main.DiscoverFragmentDirections; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.discover.TopicCluster; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.ResponseBodyUtils; @@ -375,7 +376,9 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O } private void updateSwipeRefreshState() { - binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) + ); } private void navigateToProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index c7f6d0b7..561473e0 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -369,7 +369,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre } private void updateSwipeRefreshState() { - binding.feedSwipeRefreshLayout.setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching); + AppExecutors.INSTANCE.getMainThread().execute(() -> + binding.feedSwipeRefreshLayout.setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching) + ); } private void setupFeedStories() { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt index c8dd85e2..0784b8ed 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt @@ -857,7 +857,11 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall .setLifeCycleOwner(this) .setPostFetchService(ProfilePostFetchService(profile, currentUser != null)) .setLayoutPreferences(layoutPreferences) - .addFetchStatusChangeListener { binding.swipeRefreshLayout.isRefreshing = it } + .addFetchStatusChangeListener { + AppExecutors.mainThread.execute { + binding.swipeRefreshLayout.isRefreshing = it + } + } .setFeedItemCallback(feedItemCallback) .setSelectionModeCallback(selectionModeCallback) .init() From 57ffd32c834ff19073c30909e7e24a4d4aa90deb Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 15:17:32 -0400 Subject: [PATCH 12/16] close #1605 --- .../awais/instagrabber/fragments/PostViewV2Fragment.java | 2 ++ .../awais/instagrabber/fragments/StoryViewerFragment.kt | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index acb1a7e6..8e8f62e5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -755,6 +755,8 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme popupMenu.setOnMenuItemClickListener(item -> { final int itemId = item.getItemId(); if (itemId == R.id.share_dm) { + if (profileModel.isPrivate()) + Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show(); final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections .actionGlobalUserSearch() .setTitle(getString(R.string.share)) diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt index 897f4746..e255e4a2 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt @@ -797,6 +797,13 @@ class StoryViewerFragment : Fragment() { } private fun shareStoryViaDm() { + val story = storiesViewModel.getCurrentStory().value ?: return + val context = context + if (story.user?.isPrivate == true && context != null) { + Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show() + } + val actionBar = fragmentActivity.supportActionBar + if (actionBar != null) actionBar.subtitle = null val actionGlobalUserSearch = UserSearchFragmentDirections.actionGlobalUserSearch().apply { title = getString(R.string.share) setActionLabel(getString(R.string.send)) From d106ac5c0ad888731cbdbbea65b7be0ce0b14b03 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 15:20:56 -0400 Subject: [PATCH 13/16] fix observer delegations --- .../fragments/StoryViewerFragment.kt | 16 ++++++++-------- .../fragments/main/FeedFragment.java | 10 +--------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt index e255e4a2..34d789a4 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt @@ -213,7 +213,7 @@ class StoryViewerFragment : Fragment() { storiesViewModel.setMedia(position) } binding.storiesList.adapter = storiesAdapter - storiesViewModel.getCurrentStory().observe(viewLifecycleOwner, { + storiesViewModel.getCurrentStory().observe(fragmentActivity, { if (it?.items != null && it.items.size > 1) { val storyMedias = it.items.toMutableList() val newItem = storyMedias.get(0) @@ -231,19 +231,19 @@ class StoryViewerFragment : Fragment() { binding.storiesList.visibility = View.GONE } }) - storiesViewModel.getDate().observe(viewLifecycleOwner, { + storiesViewModel.getDate().observe(fragmentActivity, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.subtitle = it }) - storiesViewModel.getTitle().observe(viewLifecycleOwner, { + storiesViewModel.getTitle().observe(fragmentActivity, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.title = it }) - storiesViewModel.getCurrentMedia().observe(viewLifecycleOwner, { refreshStory(it) }) - storiesViewModel.getCurrentIndex().observe(viewLifecycleOwner, { + storiesViewModel.getCurrentMedia().observe(fragmentActivity, { refreshStory(it) }) + storiesViewModel.getCurrentIndex().observe(fragmentActivity, { storiesAdapter!!.paginate(it) }) - storiesViewModel.getOptions().observe(viewLifecycleOwner, { + storiesViewModel.getOptions().observe(fragmentActivity, { binding.stickers.isEnabled = it.first.size > 0 }) } @@ -268,7 +268,7 @@ class StoryViewerFragment : Fragment() { when (type) { StoryViewerOptions.Type.HIGHLIGHT -> { storiesViewModel.fetchHighlights(options!!.id) - storiesViewModel.highlights.observe(viewLifecycleOwner) { + storiesViewModel.highlights.observe(fragmentActivity) { setupMultipage(it) } } @@ -319,7 +319,7 @@ class StoryViewerFragment : Fragment() { private fun setupMultipage(models: List?) { if (models == null) return - storiesViewModel.getPagination().observe(viewLifecycleOwner, { + storiesViewModel.getPagination().observe(fragmentActivity, { when (it) { StoryPaginationType.FORWARD -> { if (currentFeedStoryIndex == models.size - 1) diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index 561473e0..eb2909d2 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -319,14 +319,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre return super.onOptionsItemSelected(item); } - @Override - public void onResume() { - super.onResume(); - // temporary fix - feedStoriesViewModel.getList().removeObservers(getViewLifecycleOwner()); - feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList); - } - @Override public void onRefresh() { binding.feedRecyclerView.refresh(); @@ -382,7 +374,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre storiesRecyclerView = binding.header; storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)); storiesRecyclerView.setAdapter(feedStoriesAdapter); - feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList); + feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList); fetchStories(); } From 2082c97ed6ed0e6756242849681580719d005dad Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 16:49:50 -0400 Subject: [PATCH 14/16] close #1555 --- .../awais/instagrabber/fragments/search/SearchFragment.java | 1 + .../awais/instagrabber/viewmodels/SearchFragmentViewModel.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java b/app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java index 371793cc..96cb0191 100644 --- a/app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java @@ -239,6 +239,7 @@ public class SearchFragment extends Fragment implements SearchCategoryFragment.O switch (resource.status) { case SUCCESS: viewModel.search("", type); + viewModel.search("", FavoriteType.TOP); liveData.removeObserver(this); break; case ERROR: diff --git a/app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.java index da2d0618..45825297 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.java @@ -242,7 +242,7 @@ public class SearchFragmentViewModel extends AppStateViewModel { @Override public void onFailure(@NonNull final Throwable t) { if (!TextUtils.isEmpty(tempQuery)) return; - topResults.postValue(Resource.success(Collections.emptyList())); + liveData.postValue(Resource.success(Collections.emptyList())); Log.e(TAG, "onFailure: ", t); } }, AppExecutors.INSTANCE.getMainThread()); From 429549412afb428c0f5b5638b7b816ea285b68bb Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 18:54:16 -0400 Subject: [PATCH 15/16] fix notifications --- .../notification/NotificationCounts.kt | 14 ++--- .../services/ActivityCheckerService.java | 55 +++++++++++-------- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-eu/strings.xml | 1 - app/src/main/res/values-fa/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-mk/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-or/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 5 +- 29 files changed, 44 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt index d989dc0c..9e491168 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt @@ -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) \ No newline at end of file +class NotificationCounts(val commentLikes: Int, + val usertags: Int, + val likes: Int, + val comments: Int, + val relationships: Int, + val photosOfYou: Int, + val requests: Int) \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java index 1845188e..8de02753 100644 --- a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java +++ b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java @@ -54,10 +54,9 @@ public class ActivityCheckerService extends Service { public void onSuccess(final NotificationCounts result) { try { if (result == null) return; - final String notification = getNotificationString(result); + final List notification = getNotificationString(result); if (notification == null) return; - final String notificationString = getString(R.string.activity_count_prefix) + " " + notification + "."; - showNotification(notificationString); + showNotification(notification); } finally { handler.postDelayed(runnable, DELAY_MILLIS); } @@ -88,42 +87,54 @@ public class ActivityCheckerService extends Service { handler.removeCallbacks(runnable); } - private String getNotificationString(final NotificationCounts result) { + private List getNotificationString(final NotificationCounts result) { + final List toReturn = new ArrayList<>(2); final List list = new ArrayList<>(); - if (result.getRelationshipsCount() != 0) { - list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount())); + int count = 0; + if (result.getRelationships() != 0) { + list.add(getString(R.string.activity_count_relationship, result.getRelationships())); + count += result.getRelationships(); } - if (result.getRequestsCount() != 0) { - list.add(getString(R.string.activity_count_requests, result.getRequestsCount())); + if (result.getRequests() != 0) { + list.add(getString(R.string.activity_count_requests, result.getRequests())); + count += result.getRequests(); } - if (result.getUserTagsCount() != 0) { - list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount())); + if (result.getUsertags() != 0) { + list.add(getString(R.string.activity_count_usertags, result.getUsertags())); + count += result.getUsertags(); } - if (result.getPOYCount() != 0) { - list.add(getString(R.string.activity_count_poy, result.getPOYCount())); + if (result.getPhotosOfYou() != 0) { + list.add(getString(R.string.activity_count_poy, result.getPhotosOfYou())); + count += result.getPhotosOfYou(); } - if (result.getCommentsCount() != 0) { - list.add(getString(R.string.activity_count_comments, result.getCommentsCount())); + if (result.getComments() != 0) { + list.add(getString(R.string.activity_count_comments, result.getComments())); + count += result.getComments(); } - if (result.getCommentLikesCount() != 0) { - list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount())); + if (result.getCommentLikes() != 0) { + list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikes())); + count += result.getCommentLikes(); } - if (result.getLikesCount() != 0) { - list.add(getString(R.string.activity_count_likes, result.getLikesCount())); + if (result.getLikes() != 0) { + list.add(getString(R.string.activity_count_likes, result.getLikes())); + count += result.getLikes(); } if (list.isEmpty()) return null; - return TextUtils.join(", ", list); + toReturn.add(TextUtils.join(", ", list)); + toReturn.add(getResources().getQuantityString(R.plurals.activity_count_total, count, count)); + return toReturn; } - private void showNotification(final String notificationString) { + private void showNotification(final List notificationString) { final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID) .setCategory(NotificationCompat.CATEGORY_STATUS) .setSmallIcon(R.drawable.ic_notif) .setAutoCancel(true) .setOnlyAlertOnce(true) .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setContentTitle(getString(R.string.action_notif)) - .setContentText(notificationString) + .setContentTitle(notificationString.get(1)) + .setContentText(notificationString.get(0)) + .setStyle(new NotificationCompat.BigTextStyle().bigText(notificationString.get(0))) .setContentIntent(getPendingIntent()) .build(); notificationManager.notify(Constants.ACTIVITY_NOTIFICATION_ID, notification); diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6561c224..3b29f623 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -253,7 +253,6 @@ Suggested users Select Picture Uploading… - You have: %d follows %d comments %d comment likes diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 8bf7a8fe..e3c0f4ab 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -237,7 +237,6 @@ Usuaris suggerits Seleccionar imatge S\'està pujant… - Tens: %d seguidors %d comentaris %d m\'agrades al comentari diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index be7f21b0..2ef83896 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -245,7 +245,6 @@ Navrhovaní uživatelé Vybrat obrázek Nahrávání… - Máte: %d sleduje %d komentářů %d lajků komentáře diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4b602693..4da61562 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -237,7 +237,6 @@ Vorgeschlagene Benutzer Bild auswählen Hochladen… - Du hast: %d Abonnenten %d Kommentare %d gelikte Kommentare diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index e05c7782..096bcbf2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -237,7 +237,6 @@ Προτεινόμενοι χρήστες Επιλογή εικόνας Μεταφόρτωση… - Έχετε: %d ακόλουθοι %d σχόλια Το σχόλιο αρέσει σε %d diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 89e41e58..b16a341b 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -237,7 +237,6 @@ Usuarios sugeridos Seleccionar imagen Subiendo… - Tienes: %d sigue %d comentarios %d me gustas en comentarios diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index d26f7b3b..2f8e6281 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -237,7 +237,6 @@ Iradokitutako erabiltzaileak Hautatu irudia Igotzen… - Duzuna: %d jarraitzaile %d iruzkin %d iruzkin-atsegite diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 40c4a314..37654b7f 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -238,7 +238,6 @@ Suggested users انتخاب تصویر Uploading… - شما باید: %d دنبال کننده‌ %d دیدگاه %d پسند دیدگاه diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0402c466..87d8c124 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -237,7 +237,6 @@ Utilisateurs suggérés Sélectionnez une image Envoi en cours… - Vous avez : %d abonné(e)s %d commentaires %d j\'aime(s) sur le commentaire diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index e3ae4576..b7676cdb 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -238,7 +238,6 @@ सुझायें ऊपयोगकर्ता चित्र का चयन करें अपलोड हो रहा है... - आपके पास है: %d अनुगामी %d टिप्पणियाँ %d टिप्पणीयाँ पसन्दीत diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 5fc2a9fb..07dcc475 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -233,7 +233,6 @@ Pengguna yang disarankan Pilih Gambar Mengunggah… - Anda memiliki: %d mengikuti %d komentar %d suka komentar diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e6600907..76f0a941 100755 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -237,7 +237,6 @@ Utenti suggeriti Seleziona Immagine Caricamento… - Hai: %d seguaci %d commenti %d mi piace al commento diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 68c201db..4c14f8f5 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -233,7 +233,6 @@ おすすめのユーザー 画像を選択 アップロード中… - あなたのステータス: %d 人のフォロワー %d コメント %d 個のコメントへのいいね! diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 4de78257..4367851a 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -233,7 +233,6 @@ 프로필 추천 사진 선택 업로드 중… - You have: %d follows %d comments %d comment likes diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 5da6575f..cb8abc1c 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -237,7 +237,6 @@ Препорачани кориснчки сметки Селектирај слика Се Прикачува… - Вие имате: %d следачи %d коментари %d лајкови на коментари diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 689de451..4264eacf 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -237,7 +237,6 @@ Voorgestelde gebruikers Selecteer Afbeelding Bezig met uploaden… - Je hebt: %d volgers %d opmerkingen %d opmerking-likes diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 5119d3ec..2ae5c0bd 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -237,7 +237,6 @@ Suggested users Select Picture Uploading… - You have: %d follows %d comments %d comment likes diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9a157ce7..466deeca 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -245,7 +245,6 @@ Proponowani użytkownicy Wybierz obraz Przesyłanie… - Masz: %d obserwujących %d komentarzy %d polubionych komentarzy diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 1f9a39eb..44b57c37 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -237,7 +237,6 @@ Usuários sugeridos Selecionar imagem Enviando… - Você tem: %d seguidores %d comentários %d comentários curtidos diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 07b10200..f8a192e9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -245,7 +245,6 @@ Предлагаемые пользователи Выберите изображение Загрузка… - У вас есть: %d подписано %d комментариев %d симпатий к комментарию diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 5164bf75..9d58c57f 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -245,7 +245,6 @@ Používatelia ktorých možno poznáte Vybrať fotografiu Nahráva sa… - Máš: %d sledovaní %d komentárov %d komentárov ktoré sa niekomu páčia diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 0114f7d3..e3f8c081 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -237,7 +237,6 @@ Suggested users Välj bild Laddar upp… - Du har: %d följer %d kommentarer %d gillade kommentarer diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c146b1d2..7006a9d0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -237,7 +237,6 @@ Önerilen kullanıcılar Resim Seç Yükleniyor… - Sahip olduğun: %d takip %d yorum %d yorum beğenisi diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e23ab8f0..57c967d9 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -233,7 +233,6 @@ Người dùng được đề xuất Chọn hình ảnh Đang tải lên… - Bạn có: %d người theo dõi %d bình luận %d lượt thích bình luận diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0a3d00b6..7ff31c0f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -233,7 +233,6 @@ 推荐用户 选择图片 上传中... - 您有: %d 位新粉丝 %d 个评论回复 %d 个评论点赞 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e4c6bbb4..412c67ce 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -233,7 +233,6 @@ 推薦用戶 選擇圖片 上傳中… - 您有 %d 個追蹤者 %d 個評論 %d 個評論的讚 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c601e7f..ece231cb 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,7 +242,10 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Select Picture Uploading… - You have: + + You have %d notification + You have %d notifications + %d follows %d comments %d comment likes From db8af26b3564a1b98bdc6196e3a710eb58b087a5 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Fri, 9 Jul 2021 22:14:50 -0400 Subject: [PATCH 16/16] fix sending videos through dm --- .../awais/instagrabber/utils/MediaUtils.java | 60 ++++++++----------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/utils/MediaUtils.java b/app/src/main/java/awais/instagrabber/utils/MediaUtils.java index 0d58a76f..1642acb7 100644 --- a/app/src/main/java/awais/instagrabber/utils/MediaUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/MediaUtils.java @@ -15,48 +15,23 @@ import java.io.FileDescriptor; public final class MediaUtils { private static final String TAG = MediaUtils.class.getSimpleName(); - private static final String[] PROJECTION_VIDEO = { - MediaStore.Video.Media.DURATION, - MediaStore.Video.Media.WIDTH, - MediaStore.Video.Media.HEIGHT, - MediaStore.Video.Media.SIZE - }; - private static final String[] PROJECTION_AUDIO = { - MediaStore.Audio.Media.DURATION, - MediaStore.Audio.Media.SIZE - }; public static void getVideoInfo(@NonNull final ContentResolver contentResolver, @NonNull final Uri uri, @NonNull final OnInfoLoadListener listener) { - AppExecutors.INSTANCE.getTasksThread().submit(() -> { - try (Cursor cursor = MediaStore.Video.query(contentResolver, uri, PROJECTION_VIDEO)) { - if (cursor == null) { - listener.onLoad(null); - return; - } - int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION); - int widthColumn = cursor.getColumnIndex(MediaStore.Video.Media.WIDTH); - int heightColumn = cursor.getColumnIndex(MediaStore.Video.Media.HEIGHT); - int sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE); - if (cursor.moveToNext()) { - listener.onLoad(new VideoInfo( - cursor.getLong(durationColumn), - cursor.getInt(widthColumn), - cursor.getInt(heightColumn), - cursor.getLong(sizeColumn) - )); - } - } catch (Exception e) { - Log.e(TAG, "getVideoInfo: ", e); - listener.onFailure(e); - } - }); + getInfo(contentResolver, uri, listener, true); } public static void getVoiceInfo(@NonNull final ContentResolver contentResolver, @NonNull final Uri uri, @NonNull final OnInfoLoadListener listener) { + getInfo(contentResolver, uri, listener, false); + } + + private static void getInfo(@NonNull final ContentResolver contentResolver, + @NonNull final Uri uri, + @NonNull final OnInfoLoadListener listener, + @NonNull final Boolean isVideo) { AppExecutors.INSTANCE.getTasksThread().submit(() -> { try (ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r")) { if (parcelFileDescriptor == null) { @@ -68,6 +43,23 @@ public final class MediaUtils { mediaMetadataRetriever.setDataSource(fileDescriptor); String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); if (TextUtils.isEmpty(duration)) duration = "0"; + if (isVideo) { + String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); + if (TextUtils.isEmpty(width)) width = "1"; + String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + if (TextUtils.isEmpty(height)) height = "1"; + final Cursor cursor = contentResolver.query(uri, new String[]{MediaStore.Video.Media.SIZE}, null, null, null); + cursor.moveToFirst(); + final long fileSize = cursor.getLong(0); + cursor.close(); + listener.onLoad(new VideoInfo( + Long.parseLong(duration), + Integer.valueOf(width), + Integer.valueOf(height), + fileSize + )); + return; + } listener.onLoad(new VideoInfo( Long.parseLong(duration), 0, @@ -75,7 +67,7 @@ public final class MediaUtils { 0 )); } catch (Exception e) { - Log.e(TAG, "getVoiceInfo: ", e); + Log.e(TAG, "getInfo: ", e); listener.onFailure(e); } });