Browse Source
Merge branch 'master' into dm-notifications-enhancements
renovate/org.robolectric-robolectric-4.x
Merge branch 'master' into dm-notifications-enhancements
renovate/org.robolectric-robolectric-4.x
187 changed files with 8194 additions and 5041 deletions
-
101.all-contributorsrc
-
42README.md
-
8app/build.gradle
-
16app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
-
11app/src/main/java/awais/instagrabber/adapters/FeedStoriesAdapter.java
-
59app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java
-
53app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java
-
45app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
-
15app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
-
75app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java
-
1app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
-
48app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
-
240app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
-
85app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
-
3app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
-
3app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
-
47app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
-
33app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
-
93app/src/main/java/awais/instagrabber/asyncs/FeedStoriesFetcher.java
-
21app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
-
1app/src/main/java/awais/instagrabber/asyncs/HashtagFetcher.java
-
19app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
-
73app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java
-
19app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
-
104app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
-
1app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
-
1app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
-
19app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
-
27app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
-
89app/src/main/java/awais/instagrabber/asyncs/QuizAction.java
-
88app/src/main/java/awais/instagrabber/asyncs/RespondAction.java
-
24app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
-
69app/src/main/java/awais/instagrabber/asyncs/VoteAction.java
-
127app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
-
6app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
-
2app/src/main/java/awais/instagrabber/db/AppDatabase.java
-
2app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.java
-
50app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
-
154app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
-
106app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
-
176app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
-
48app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
170app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java
-
23app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
135app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
-
85app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
12app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
-
220app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
-
383app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
-
11app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
52app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
272app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
7app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java
-
20app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java
-
13app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
-
20app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
-
14app/src/main/java/awais/instagrabber/models/BasePostModel.java
-
10app/src/main/java/awais/instagrabber/models/CommentModel.java
-
14app/src/main/java/awais/instagrabber/models/FeedModel.java
-
48app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
-
2app/src/main/java/awais/instagrabber/models/HashtagModel.java
-
27app/src/main/java/awais/instagrabber/models/HighlightModel.java
-
2app/src/main/java/awais/instagrabber/models/LocationModel.java
-
14app/src/main/java/awais/instagrabber/models/NotificationModel.java
-
2app/src/main/java/awais/instagrabber/models/PostModel.java
-
31app/src/main/java/awais/instagrabber/models/ProfileModel.java
-
22app/src/main/java/awais/instagrabber/models/StoryModel.java
-
10app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
-
53app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java
-
10app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
-
12app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java
-
3app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
-
33app/src/main/java/awais/instagrabber/repositories/MediaRepository.java
-
16app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
-
6app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
-
27app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
-
3app/src/main/java/awais/instagrabber/repositories/TagsRepository.java
-
79app/src/main/java/awais/instagrabber/repositories/responses/GraphQLUserListFetchResponse.java
-
20app/src/main/java/awais/instagrabber/repositories/responses/StoryStickerResponse.java
-
16app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java
-
6app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectUser.java
-
5app/src/main/java/awais/instagrabber/utils/Constants.java
-
2app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
-
4app/src/main/java/awais/instagrabber/utils/LocaleUtils.java
-
381app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
3app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
7app/src/main/java/awais/instagrabber/utils/Utils.java
-
19app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java
-
8app/src/main/java/awais/instagrabber/viewmodels/BasePostViewModel.java
-
1app/src/main/java/awais/instagrabber/viewmodels/FeedStoriesViewModel.java
-
19app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.java
-
9app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.java
-
1app/src/main/java/awais/instagrabber/webservices/DiscoverService.java
-
108app/src/main/java/awais/instagrabber/webservices/FeedService.java
-
4app/src/main/java/awais/instagrabber/webservices/FriendshipService.java
-
243app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
-
193app/src/main/java/awais/instagrabber/webservices/LocationService.java
-
166app/src/main/java/awais/instagrabber/webservices/MediaService.java
@ -9,7 +9,7 @@ |
|||
[](http://makeapullrequest.com) |
|||
[](./LICENSE) |
|||
[](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> |
|||
[](#contributors) |
|||
[](#contributors) |
|||
<!-- ALL-CONTRIBUTORS-BADGE:END --> |
|||
|
|||
We're previously known as InstaGrabber. |
|||
@ -32,11 +32,15 @@ Version status: . |
|||
|
|||
## Contact us |
|||
|
|||
* Use [GitHub issues](https://github.com/austinhuang0131/instagrabber/issues) when possible. |
|||
* Email: [[email protected]](mailto:[email protected]?body=Please%20note%20that%20your%20email%20address%20and%20the%20entire%20content%20will%20be%20published%20onto%20GitHub%20issues.%20If%20you%20do%20not%20wish%20to%20do%20that%2C%20use%20other%20contact%20methods%20instead.) (Synced to GitHub issues) |
|||
* Reddit: [r/Barinsta](https://reddit.com/r/barinsta) |
|||
* Reddit: [](https://reddit.com/r/barinsta) |
|||
* Chat (Bridged to each other): [](https://matrix.to/#/#barinsta:matrix.org) [](https://t.me/grabber_app) [](https://discord.gg/YtEDzN2) [](https://gitter.im/instagrabber/general) |
|||
|
|||
## Contributors |
|||
@ -53,36 +57,43 @@ Prominent contributors are listed here in the [all-contributors](https://allcont |
|||
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td> |
|||
<td align="center"><a href="http://rerolledgeek.blogspot.com/"><img src="https://avatars3.githubusercontent.com/u/5278488?s=100" width="100px;" alt=""/><br /><sub><b>AWAiS</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=AwaisKing" title="Code">💻</a></td> |
|||
<td align="center"><a href="https://stefannajdovski.com/"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td> |
|||
<td align="center"><a href="https://github.com/CrazyMarvin"><img src="https://avatars3.githubusercontent.com/u/15004217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CrazyMarvin</b></sub></a><br /><a href="#financial-CrazyMarvin" title="Financial">💵</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td> |
|||
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td> |
|||
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td> |
|||
<td align="center"><a href="https://alin.atwebpages.com/"><img src="https://avatars2.githubusercontent.com/u/13281020?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ALIN</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aalin-1" title="Bug reports">🐛</a> <a href="#ideas-alin-1" title="Ideas, Planning, & Feedback">🤔</a></td> |
|||
<td align="center"><a href="https://github.com/RickyM7"><img src="https://avatars3.githubusercontent.com/u/24703825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3ARickyM7" title="Bug reports">🐛</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td> |
|||
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://github.com/cizordj"><img src="https://avatars2.githubusercontent.com/u/32869222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cézar Augusto</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/initdebugs"><img src="https://avatars0.githubusercontent.com/u/75781464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Initdebugs</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://janek.xyz/"><img src="https://avatars3.githubusercontent.com/u/8365659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Janek</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/rohang02"><img src="https://avatars3.githubusercontent.com/u/47921164?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rohang02</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/retiolus"><img src="https://avatars1.githubusercontent.com/u/65604466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>retiolus</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/RickyM7"><img src="https://avatars3.githubusercontent.com/u/24703825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3ARickyM7" title="Bug reports">🐛</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://github.com/rikishi0071"><img src="https://avatars3.githubusercontent.com/u/18183855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rikishi0071</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://stillu.cc/"><img src="https://avatars2.githubusercontent.com/u/5843208?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Still Hsu</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
<tr> |
|||
<td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
<td align="center"><a href="https://github.com/ysakamoto"><img src="https://avatars3.githubusercontent.com/u/1331642?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ysakamoto</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> |
|||
</tr> |
|||
</table> |
|||
|
|||
@ -92,11 +103,10 @@ Prominent contributors are listed here in the [all-contributors](https://allcont |
|||
|
|||
## License |
|||
|
|||
This app is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber). |
|||
This app's predecessor, InstaGrabber, is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber). |
|||
|
|||
Barinsta (ex-InstaGrabber) |
|||
Copyright (C) 2019 AWAiS <chapter50000@hotmail.com> |
|||
Copyright (C) 2020 Austin Huang <im@austinhuang.me> |
|||
Barinsta |
|||
Copyright (C) 2020-2021 Austin Huang <im@austinhuang.me> |
|||
Ammar Githam <ammargitham786@gmail.com> |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
|
@ -0,0 +1,59 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.view.LayoutInflater; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.recyclerview.widget.DiffUtil; |
|||
import androidx.recyclerview.widget.ListAdapter; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.adapters.viewholder.StoryListViewHolder; |
|||
import awais.instagrabber.databinding.ItemNotificationBinding; |
|||
import awais.instagrabber.models.FeedStoryModel; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, StoryListViewHolder> { |
|||
private final OnFeedStoryClickListener listener; |
|||
|
|||
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() { |
|||
@Override |
|||
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { |
|||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) { |
|||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead()); |
|||
} |
|||
}; |
|||
|
|||
public FeedStoriesListAdapter(final OnFeedStoryClickListener listener) { |
|||
super(diffCallback); |
|||
this.listener = listener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false); |
|||
return new StoryListViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) { |
|||
final FeedStoryModel model = getItem(position); |
|||
holder.bind(model, position, listener); |
|||
} |
|||
|
|||
public interface OnFeedStoryClickListener { |
|||
void onFeedStoryClick(final FeedStoryModel model, final int position); |
|||
|
|||
void onProfileClick(final String username); |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.view.LayoutInflater; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.DiffUtil; |
|||
import androidx.recyclerview.widget.ListAdapter; |
|||
|
|||
import awais.instagrabber.adapters.viewholder.StoryListViewHolder; |
|||
import awais.instagrabber.databinding.ItemNotificationBinding; |
|||
import awais.instagrabber.models.HighlightModel; |
|||
|
|||
public final class HighlightStoriesListAdapter extends ListAdapter<HighlightModel, StoryListViewHolder> { |
|||
private final OnHighlightStoryClickListener listener; |
|||
|
|||
private static final DiffUtil.ItemCallback<HighlightModel> diffCallback = new DiffUtil.ItemCallback<HighlightModel>() { |
|||
@Override |
|||
public boolean areItemsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) { |
|||
return oldItem.getId().equals(newItem.getId()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean areContentsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) { |
|||
return oldItem.getId().equals(newItem.getId()); |
|||
} |
|||
}; |
|||
|
|||
public HighlightStoriesListAdapter(final OnHighlightStoryClickListener listener) { |
|||
super(diffCallback); |
|||
this.listener = listener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false); |
|||
return new StoryListViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) { |
|||
final HighlightModel model = getItem(position); |
|||
holder.bind(model, position, listener); |
|||
} |
|||
|
|||
public interface OnHighlightStoryClickListener { |
|||
void onHighlightClick(final HighlightModel model, final int position); |
|||
|
|||
void onProfileClick(final String username); |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.util.Log; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.adapters.viewholder.FollowsViewHolder; |
|||
import awais.instagrabber.databinding.ItemFollowBinding; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
|
|||
public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> { |
|||
private final List<ProfileModel> profileModels; |
|||
private final View.OnClickListener onClickListener; |
|||
|
|||
public LikesAdapter(final List<ProfileModel> profileModels, |
|||
final View.OnClickListener onClickListener) { |
|||
this.profileModels = profileModels; |
|||
this.onClickListener = onClickListener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public FollowsViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false); |
|||
return new FollowsViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) { |
|||
final ProfileModel model = profileModels.get(position); |
|||
holder.bind(model, null, onClickListener); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return profileModels.size(); |
|||
} |
|||
} |
@ -1,75 +0,0 @@ |
|||
// package awais.instagrabber.adapters; |
|||
// |
|||
// import android.view.LayoutInflater; |
|||
// import android.view.View; |
|||
// import android.view.ViewGroup; |
|||
// |
|||
// import androidx.annotation.NonNull; |
|||
// import androidx.recyclerview.widget.DiffUtil; |
|||
// import androidx.recyclerview.widget.ListAdapter; |
|||
// |
|||
// import awais.instagrabber.adapters.viewholder.PostViewerViewHolder; |
|||
// import awais.instagrabber.databinding.ItemFullPostViewBinding; |
|||
// import awais.instagrabber.interfaces.MentionClickListener; |
|||
// import awais.instagrabber.models.ViewerPostModelWrapper; |
|||
// |
|||
// public class PostViewAdapter extends ListAdapter<ViewerPostModelWrapper, PostViewerViewHolder> { |
|||
// private final OnPostViewChildViewClickListener clickListener; |
|||
// private final OnPostCaptionLongClickListener longClickListener; |
|||
// private final MentionClickListener mentionClickListener; |
|||
// |
|||
// private static final DiffUtil.ItemCallback<ViewerPostModelWrapper> diffCallback = new DiffUtil.ItemCallback<ViewerPostModelWrapper>() { |
|||
// @Override |
|||
// public boolean areItemsTheSame(@NonNull final ViewerPostModelWrapper oldItem, |
|||
// @NonNull final ViewerPostModelWrapper newItem) { |
|||
// return oldItem.getPosition() == newItem.getPosition(); |
|||
// } |
|||
// |
|||
// @Override |
|||
// public boolean areContentsTheSame(@NonNull final ViewerPostModelWrapper oldItem, |
|||
// @NonNull final ViewerPostModelWrapper newItem) { |
|||
// return oldItem.getViewerPostModels().equals(newItem.getViewerPostModels()); |
|||
// } |
|||
// }; |
|||
// |
|||
// public PostViewAdapter(final OnPostViewChildViewClickListener clickListener, |
|||
// final OnPostCaptionLongClickListener longClickListener, |
|||
// final MentionClickListener mentionClickListener) { |
|||
// super(diffCallback); |
|||
// this.clickListener = clickListener; |
|||
// this.longClickListener = longClickListener; |
|||
// this.mentionClickListener = mentionClickListener; |
|||
// } |
|||
// |
|||
// @Override |
|||
// public void onViewDetachedFromWindow(@NonNull final PostViewerViewHolder holder) { |
|||
// holder.stopPlayingVideo(); |
|||
// } |
|||
// |
|||
// @NonNull |
|||
// @Override |
|||
// public PostViewerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, |
|||
// final int viewType) { |
|||
// final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
// final ItemFullPostViewBinding binding = ItemFullPostViewBinding |
|||
// .inflate(layoutInflater, parent, false); |
|||
// return new PostViewerViewHolder(binding); |
|||
// } |
|||
// |
|||
// @Override |
|||
// public void onBindViewHolder(@NonNull final PostViewerViewHolder holder, final int position) { |
|||
// final ViewerPostModelWrapper item = getItem(position); |
|||
// holder.bind(item, position, clickListener, longClickListener, mentionClickListener); |
|||
// } |
|||
// |
|||
// public interface OnPostViewChildViewClickListener { |
|||
// void onClick(View v, |
|||
// ViewerPostModelWrapper viewerPostModelWrapper, |
|||
// int postPosition, |
|||
// int childPosition); |
|||
// } |
|||
// |
|||
// public interface OnPostCaptionLongClickListener { |
|||
// void onLongClick(String text); |
|||
// } |
|||
// } |
@ -1,240 +0,0 @@ |
|||
// package awais.instagrabber.adapters.viewholder; |
|||
// |
|||
// import android.content.res.ColorStateList; |
|||
// import android.content.res.Resources; |
|||
// import android.util.Log; |
|||
// import android.view.View; |
|||
// import android.widget.TextView; |
|||
// |
|||
// import androidx.annotation.NonNull; |
|||
// import androidx.core.content.ContextCompat; |
|||
// import androidx.core.view.ViewCompat; |
|||
// import androidx.recyclerview.widget.RecyclerView; |
|||
// import androidx.viewpager2.widget.ViewPager2; |
|||
// |
|||
// import com.google.android.exoplayer2.Player; |
|||
// import com.google.android.exoplayer2.SimpleExoPlayer; |
|||
// import com.google.android.exoplayer2.ui.PlayerView; |
|||
// |
|||
// import java.util.ArrayList; |
|||
// import java.util.List; |
|||
// |
|||
// import awais.instagrabber.R; |
|||
// import awais.instagrabber.adapters.PostViewAdapter.OnPostCaptionLongClickListener; |
|||
// import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener; |
|||
// import awais.instagrabber.adapters.PostViewerChildAdapter; |
|||
// import awais.instagrabber.databinding.ItemFullPostViewBinding; |
|||
// import awais.instagrabber.interfaces.MentionClickListener; |
|||
// import awais.instagrabber.models.PostChild; |
|||
// import awais.instagrabber.models.ProfileModel; |
|||
// import awais.instagrabber.models.ViewerPostModel; |
|||
// import awais.instagrabber.models.ViewerPostModelWrapper; |
|||
// import awais.instagrabber.models.enums.MediaItemType; |
|||
// import awais.instagrabber.utils.TextUtils; |
|||
// import awais.instagrabber.utils.Utils; |
|||
// |
|||
// public class PostViewerViewHolder extends RecyclerView.ViewHolder { |
|||
// private static final String TAG = "PostViewerViewHolder"; |
|||
// |
|||
// private final ItemFullPostViewBinding binding; |
|||
// private int currentChildPosition; |
|||
// |
|||
// public PostViewerViewHolder(@NonNull final ItemFullPostViewBinding binding) { |
|||
// super(binding.getRoot()); |
|||
// this.binding = binding; |
|||
// binding.topPanel.viewStoryPost.setVisibility(View.GONE); |
|||
// } |
|||
// |
|||
// public void bind(final ViewerPostModelWrapper wrapper, |
|||
// final int position, |
|||
// final OnPostViewChildViewClickListener clickListener, |
|||
// final OnPostCaptionLongClickListener longClickListener, |
|||
// final MentionClickListener mentionClickListener) { |
|||
// if (wrapper == null) return; |
|||
// final List<PostChild> items = wrapper.getViewerPostModels(); |
|||
// if (items == null || items.size() == 0) return; |
|||
// if (items.get(0) == null) return; |
|||
// final PostViewerChildAdapter adapter = new PostViewerChildAdapter(); |
|||
// binding.mediaViewPager.setAdapter(adapter); |
|||
// final PostChild firstPost = items.get(0); |
|||
// setPostInfo(firstPost, mentionClickListener); |
|||
// setMediaItems(items, adapter); |
|||
// setupListeners(wrapper, |
|||
// position, |
|||
// clickListener, |
|||
// longClickListener, |
|||
// mentionClickListener, |
|||
// firstPost.getLocation()); |
|||
// } |
|||
// |
|||
// private void setPostInfo(final PostChild firstPost, |
|||
// final MentionClickListener mentionClickListener) { |
|||
// final ProfileModel profileModel = firstPost.getProfileModel(); |
|||
// if (profileModel == null) return; |
|||
// binding.topPanel.title.setText(profileModel.getUsername()); |
|||
// final String locationName = firstPost.getLocationName(); |
|||
// if (!TextUtils.isEmpty(locationName)) { |
|||
// binding.topPanel.location.setVisibility(View.VISIBLE); |
|||
// binding.topPanel.location.setText(locationName); |
|||
// } else binding.topPanel.location.setVisibility(View.GONE); |
|||
// binding.topPanel.ivProfilePic.setImageURI(profileModel.getSdProfilePic()); |
|||
// binding.bottomPanel.commentsCount.setText(String.valueOf(firstPost.getCommentsCount())); |
|||
// final CharSequence postCaption = firstPost.getPostCaption(); |
|||
// if (TextUtils.hasMentions(postCaption)) { |
|||
// binding.bottomPanel.viewerCaption.setMentionClickListener(mentionClickListener); |
|||
// binding.bottomPanel.viewerCaption |
|||
// .setText(TextUtils.getMentionText(postCaption), TextView.BufferType.SPANNABLE); |
|||
// } else { |
|||
// binding.bottomPanel.viewerCaption.setMentionClickListener(null); |
|||
// binding.bottomPanel.viewerCaption.setText(postCaption); |
|||
// } |
|||
// binding.bottomPanel.tvPostDate.setText(firstPost.getPostDate()); |
|||
// setupLikes(firstPost); |
|||
// setupSave(firstPost); |
|||
// } |
|||
// |
|||
// private void setupLikes(final ViewerPostModel firstPost) { |
|||
// final boolean liked = firstPost.getLike(); |
|||
// final long likeCount = firstPost.getLikes(); |
|||
// final Resources resources = itemView.getContext().getResources(); |
|||
// if (liked) { |
|||
// final String unlikeString = resources.getString(R.string.unlike, String.valueOf(likeCount)); |
|||
// binding.btnLike.setText(unlikeString); |
|||
// ViewCompat.setBackgroundTintList(binding.btnLike, |
|||
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_pink_background))); |
|||
// } else { |
|||
// final String likeString = resources.getString(R.string.like, String.valueOf(likeCount)); |
|||
// binding.btnLike.setText(likeString); |
|||
// ViewCompat.setBackgroundTintList(binding.btnLike, |
|||
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightpink_background))); |
|||
// } |
|||
// } |
|||
// |
|||
// private void setupSave(final ViewerPostModel firstPost) { |
|||
// final boolean saved = firstPost.isSaved(); |
|||
// if (saved) { |
|||
// binding.btnBookmark.setText(R.string.unbookmark); |
|||
// ViewCompat.setBackgroundTintList(binding.btnBookmark, |
|||
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_orange_background))); |
|||
// } else { |
|||
// binding.btnBookmark.setText(R.string.bookmark); |
|||
// ViewCompat.setBackgroundTintList( |
|||
// binding.btnBookmark, |
|||
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightorange_background))); |
|||
// } |
|||
// } |
|||
// |
|||
// private void setupListeners(final ViewerPostModelWrapper wrapper, |
|||
// final int position, |
|||
// final OnPostViewChildViewClickListener clickListener, |
|||
// final OnPostCaptionLongClickListener longClickListener, |
|||
// final MentionClickListener mentionClickListener, |
|||
// final String location) { |
|||
// final View.OnClickListener onClickListener = v -> clickListener |
|||
// .onClick(v, wrapper, position, currentChildPosition); |
|||
// binding.bottomPanel.btnComments.setOnClickListener(onClickListener); |
|||
// binding.topPanel.title.setOnClickListener(onClickListener); |
|||
// binding.topPanel.ivProfilePic.setOnClickListener(onClickListener); |
|||
// binding.bottomPanel.btnDownload.setOnClickListener(onClickListener); |
|||
// binding.bottomPanel.viewerCaption.setOnClickListener(onClickListener); |
|||
// binding.btnLike.setOnClickListener(onClickListener); |
|||
// binding.btnBookmark.setOnClickListener(onClickListener); |
|||
// binding.bottomPanel.viewerCaption.setOnLongClickListener(v -> { |
|||
// longClickListener.onLongClick(binding.bottomPanel.viewerCaption.getText().toString()); |
|||
// return true; |
|||
// }); |
|||
// if (!TextUtils.isEmpty(location)) { |
|||
// binding.topPanel.location.setOnClickListener(v -> mentionClickListener |
|||
// .onClick(binding.topPanel.location, location, false, true)); |
|||
// } |
|||
// } |
|||
// |
|||
// private void setMediaItems(final List<ViewerPostModel> items, |
|||
// final PostViewerChildAdapter adapter) { |
|||
// final List<ViewerPostModel> filteredList = new ArrayList<>(); |
|||
// for (final ViewerPostModel model : items) { |
|||
// final MediaItemType itemType = model.getItemType(); |
|||
// if (itemType == MediaItemType.MEDIA_TYPE_VIDEO || itemType == MediaItemType.MEDIA_TYPE_IMAGE) { |
|||
// filteredList.add(model); |
|||
// } |
|||
// } |
|||
// binding.mediaCounter.setVisibility(filteredList.size() > 1 ? View.VISIBLE : View.GONE); |
|||
// final String counter = "1/" + filteredList.size(); |
|||
// binding.mediaCounter.setText(counter); |
|||
// adapter.submitList(filteredList); |
|||
// binding.mediaViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { |
|||
// @Override |
|||
// public void onPageSelected(final int position) { |
|||
// if (filteredList.size() <= 0 || position >= filteredList.size()) return; |
|||
// currentChildPosition = position; |
|||
// final String counter = (position + 1) + "/" + filteredList.size(); |
|||
// binding.mediaCounter.setText(counter); |
|||
// final ViewerPostModel viewerPostModel = filteredList.get(position); |
|||
// if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) { |
|||
// setVideoDetails(viewerPostModel); |
|||
// setVolumeListener(position); |
|||
// return; |
|||
// } |
|||
// setImageDetails(); |
|||
// } |
|||
// }); |
|||
// } |
|||
// |
|||
// private void setVolumeListener(final int position) { |
|||
// binding.bottomPanel.btnMute.setOnClickListener(v -> { |
|||
// try { |
|||
// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager |
|||
// .getChildAt(0)).findViewHolderForAdapterPosition(position); |
|||
// if (viewHolder != null) { |
|||
// final View itemView = viewHolder.itemView; |
|||
// if (itemView instanceof PlayerView) { |
|||
// final SimpleExoPlayer player = (SimpleExoPlayer) ((PlayerView) itemView) |
|||
// .getPlayer(); |
|||
// if (player == null) return; |
|||
// final float vol = player.getVolume() == 0f ? 1f : 0f; |
|||
// player.setVolume(vol); |
|||
// binding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 |
|||
// : R.drawable.ic_volume_off_24); |
|||
// Utils.sessionVolumeFull = vol == 1f; |
|||
// } |
|||
// } |
|||
// } catch (Exception e) { |
|||
// Log.e(TAG, "Error", e); |
|||
// } |
|||
// }); |
|||
// } |
|||
// |
|||
// private void setImageDetails() { |
|||
// binding.bottomPanel.btnMute.setVisibility(View.GONE); |
|||
// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE); |
|||
// } |
|||
// |
|||
// private void setVideoDetails(final ViewerPostModel viewerPostModel) { |
|||
// binding.bottomPanel.btnMute.setVisibility(View.VISIBLE); |
|||
// final long videoViews = viewerPostModel.getVideoViews(); |
|||
// if (videoViews < 0) { |
|||
// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE); |
|||
// return; |
|||
// } |
|||
// binding.bottomPanel.tvVideoViews.setText(String.valueOf(videoViews)); |
|||
// binding.bottomPanel.videoViewsContainer.setVisibility(View.VISIBLE); |
|||
// } |
|||
// |
|||
// public void stopPlayingVideo() { |
|||
// try { |
|||
// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager |
|||
// .getChildAt(0)).findViewHolderForAdapterPosition(currentChildPosition); |
|||
// if (viewHolder != null) { |
|||
// final View itemView = viewHolder.itemView; |
|||
// if (itemView instanceof PlayerView) { |
|||
// final Player player = ((PlayerView) itemView).getPlayer(); |
|||
// if (player != null) { |
|||
// player.setPlayWhenReady(false); |
|||
// } |
|||
// } |
|||
// } |
|||
// } catch (Exception e) { |
|||
// Log.e(TAG, "Error", e); |
|||
// } |
|||
// } |
|||
// } |
@ -0,0 +1,85 @@ |
|||
package awais.instagrabber.adapters.viewholder; |
|||
|
|||
import android.text.TextUtils; |
|||
import android.view.View; |
|||
|
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener; |
|||
import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener; |
|||
import awais.instagrabber.databinding.ItemNotificationBinding; |
|||
import awais.instagrabber.models.FeedStoryModel; |
|||
import awais.instagrabber.models.HighlightModel; |
|||
|
|||
public final class StoryListViewHolder extends RecyclerView.ViewHolder { |
|||
private final ItemNotificationBinding binding; |
|||
|
|||
public StoryListViewHolder(final ItemNotificationBinding binding) { |
|||
super(binding.getRoot()); |
|||
this.binding = binding; |
|||
} |
|||
|
|||
public void bind(final FeedStoryModel model, |
|||
final int position, |
|||
final OnFeedStoryClickListener notificationClickListener) { |
|||
if (model == null) return; |
|||
|
|||
final int storiesCount = model.getMediaCount(); |
|||
binding.tvComment.setVisibility(View.VISIBLE); |
|||
binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount)); |
|||
|
|||
binding.tvSubComment.setVisibility(View.GONE); |
|||
|
|||
binding.tvDate.setText(model.getDateTime()); |
|||
|
|||
binding.tvUsername.setText(model.getProfileModel().getUsername()); |
|||
binding.ivProfilePic.setImageURI(model.getProfileModel().getSdProfilePic()); |
|||
binding.ivProfilePic.setOnClickListener(v -> { |
|||
if (notificationClickListener == null) return; |
|||
notificationClickListener.onProfileClick(model.getProfileModel().getUsername()); |
|||
}); |
|||
|
|||
if (model.getFirstStoryModel() != null) { |
|||
binding.ivPreviewPic.setVisibility(View.VISIBLE); |
|||
binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail()); |
|||
} |
|||
else binding.ivPreviewPic.setVisibility(View.INVISIBLE); |
|||
|
|||
float alpha = model.isFullyRead() ? 0.5F : 1.0F; |
|||
binding.ivProfilePic.setAlpha(alpha); |
|||
binding.ivPreviewPic.setAlpha(alpha); |
|||
binding.tvUsername.setAlpha(alpha); |
|||
binding.tvComment.setAlpha(alpha); |
|||
binding.tvDate.setAlpha(alpha); |
|||
|
|||
itemView.setOnClickListener(v -> { |
|||
if (notificationClickListener == null) return; |
|||
notificationClickListener.onFeedStoryClick(model, position); |
|||
}); |
|||
} |
|||
|
|||
public void bind(final HighlightModel model, |
|||
final int position, |
|||
final OnHighlightStoryClickListener notificationClickListener) { |
|||
if (model == null) return; |
|||
|
|||
final int storiesCount = model.getMediaCount(); |
|||
binding.tvComment.setVisibility(View.VISIBLE); |
|||
binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount)); |
|||
|
|||
binding.tvSubComment.setVisibility(View.GONE); |
|||
|
|||
binding.tvUsername.setText(model.getDateTime()); |
|||
|
|||
binding.ivProfilePic.setVisibility(View.GONE); |
|||
|
|||
binding.ivPreviewPic.setVisibility(View.VISIBLE); |
|||
binding.ivPreviewPic.setImageURI(model.getThumbnailUrl()); |
|||
|
|||
itemView.setOnClickListener(v -> { |
|||
if (notificationClickListener == null) return; |
|||
notificationClickListener.onHighlightClick(model, position); |
|||
}); |
|||
} |
|||
} |
@ -1,93 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.FeedStoryModel; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.NetworkUtils; |
|||
import awaisomereport.LogCollector.LogFile; |
|||
|
|||
import static awais.instagrabber.utils.Utils.logCollector; |
|||
|
|||
public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryModel[]> { |
|||
private final FetchListener<FeedStoryModel[]> fetchListener; |
|||
|
|||
public FeedStoriesFetcher(final FetchListener<FeedStoryModel[]> fetchListener) { |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected FeedStoryModel[] doInBackground(final Void... voids) { |
|||
FeedStoryModel[] result = null; |
|||
String url = "https://www.instagram.com/graphql/query/?query_hash=b7b84d884400bc5aa7cfe12ae843a091&variables=" + |
|||
"{\"only_stories\":true,\"stories_prefetch\":false,\"stories_video_dash_manifest\":false}"; |
|||
|
|||
try { |
|||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); |
|||
conn.setInstanceFollowRedirects(false); |
|||
conn.setUseCaches(false); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
final JSONArray feedStoriesReel = new JSONObject(NetworkUtils.readFromConnection(conn)) |
|||
.getJSONObject("data") |
|||
.getJSONObject(Constants.EXTRAS_USER) |
|||
.getJSONObject("feed_reels_tray") |
|||
.getJSONObject("edge_reels_tray_to_reel") |
|||
.getJSONArray("edges"); |
|||
|
|||
conn.disconnect(); |
|||
|
|||
final int storiesLen = feedStoriesReel.length(); |
|||
final FeedStoryModel[] feedStoryModels = new FeedStoryModel[storiesLen]; |
|||
final String[] feedStoryIDs = new String[storiesLen]; |
|||
|
|||
for (int i = 0; i < storiesLen; ++i) { |
|||
final JSONObject node = feedStoriesReel.getJSONObject(i).getJSONObject("node"); |
|||
|
|||
final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner"); |
|||
final ProfileModel profileModel = new ProfileModel(false, false, false, |
|||
user.getString("id"), |
|||
user.getString("username"), |
|||
null, null, null, |
|||
user.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false); |
|||
|
|||
final String id = node.getString("id"); |
|||
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media"); |
|||
feedStoryIDs[i] = id; |
|||
feedStoryModels[i] = new FeedStoryModel(id, profileModel, fullyRead); |
|||
} |
|||
result = feedStoryModels; |
|||
} |
|||
|
|||
conn.disconnect(); |
|||
} catch (final Exception e) { |
|||
if (logCollector != null) |
|||
logCollector.appendException(e, LogFile.ASYNC_FEED_STORY_FETCHER, "doInBackground"); |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
if (fetchListener != null) fetchListener.doBefore(); |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final FeedStoryModel[] result) { |
|||
if (fetchListener != null) fetchListener.onResult(result); |
|||
} |
|||
} |
@ -1,73 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.HighlightModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.NetworkUtils; |
|||
|
|||
public final class HighlightsFetcher extends AsyncTask<Void, Void, List<HighlightModel>> { |
|||
private final String id; |
|||
private final FetchListener<List<HighlightModel>> fetchListener; |
|||
|
|||
public HighlightsFetcher(final String id, final FetchListener<List<HighlightModel>> fetchListener) { |
|||
this.id = id; |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected List<HighlightModel> doInBackground(final Void... voids) { |
|||
List<HighlightModel> result = null; |
|||
String url = "https://i.instagram.com/api/v1/highlights/" + id + "/highlights_tray/"; |
|||
|
|||
try { |
|||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); |
|||
conn.setInstanceFollowRedirects(false); |
|||
conn.setUseCaches(false); |
|||
conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
final JSONArray highlightsReel = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONArray("tray"); |
|||
|
|||
final int length = highlightsReel.length(); |
|||
final List<HighlightModel> highlightModels = new ArrayList<>(); |
|||
// final String[] highlightIds = new String[length]; |
|||
for (int i = 0; i < length; ++i) { |
|||
final JSONObject highlightNode = highlightsReel.getJSONObject(i); |
|||
highlightModels.add(new HighlightModel( |
|||
highlightNode.getString("title"), |
|||
highlightNode.getString(Constants.EXTRAS_ID), |
|||
highlightNode.getJSONObject("cover_media") |
|||
.getJSONObject("cropped_image_version") |
|||
.getString("url") |
|||
)); |
|||
} |
|||
conn.disconnect(); |
|||
result = highlightModels; |
|||
} |
|||
|
|||
conn.disconnect(); |
|||
} catch (Exception e) { |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final List<HighlightModel> result) { |
|||
if (fetchListener != null) fetchListener.onResult(result); |
|||
} |
|||
} |
@ -1,89 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.DataOutputStream; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.util.UUID; |
|||
|
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.stickers.QuizModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class QuizAction extends AsyncTask<Integer, Void, Integer> { |
|||
private static final String TAG = "QuizAction"; |
|||
|
|||
private final StoryModel storyModel; |
|||
private final QuizModel quizModel; |
|||
private final String cookie; |
|||
private final OnTaskCompleteListener onTaskCompleteListener; |
|||
|
|||
public QuizAction(final StoryModel storyModel, |
|||
final QuizModel quizModel, |
|||
final String cookie, |
|||
final OnTaskCompleteListener onTaskCompleteListener) { |
|||
this.storyModel = storyModel; |
|||
this.quizModel = quizModel; |
|||
this.cookie = cookie; |
|||
this.onTaskCompleteListener = onTaskCompleteListener; |
|||
} |
|||
|
|||
protected Integer doInBackground(Integer... rawChoice) { |
|||
int choice = rawChoice[0]; |
|||
final String url = "https://i.instagram.com/api/v1/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + quizModel.getId() + "/story_quiz_answer/"; |
|||
HttpURLConnection urlConnection = null; |
|||
try { |
|||
JSONObject ogBody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString() |
|||
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString() |
|||
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] |
|||
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie) |
|||
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) |
|||
+ "\"}"); |
|||
ogBody.put("answer", String.valueOf(choice)); |
|||
String urlParameters = Utils.sign(ogBody.toString()); |
|||
urlConnection = (HttpURLConnection) new URL(url).openConnection(); |
|||
urlConnection.setRequestMethod("POST"); |
|||
urlConnection.setUseCaches(false); |
|||
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); |
|||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
|||
if (urlParameters != null) { |
|||
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length)); |
|||
} |
|||
urlConnection.setDoOutput(true); |
|||
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); |
|||
wr.writeBytes(urlParameters); |
|||
wr.flush(); |
|||
wr.close(); |
|||
Log.d(TAG, "quiz: " + url + " " + cookie + " " + urlParameters); |
|||
urlConnection.connect(); |
|||
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
return choice; |
|||
} |
|||
} catch (Throwable ex) { |
|||
Log.e(TAG, "quiz: " + ex); |
|||
} finally { |
|||
if (urlConnection != null) { |
|||
urlConnection.disconnect(); |
|||
} |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final Integer choice) { |
|||
if (onTaskCompleteListener == null || choice == null) return; |
|||
onTaskCompleteListener.onTaskComplete(choice); |
|||
} |
|||
|
|||
public interface OnTaskCompleteListener { |
|||
void onTaskComplete(final int choice); |
|||
} |
|||
} |
@ -1,88 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.DataOutputStream; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.util.UUID; |
|||
|
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.stickers.QuestionModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class RespondAction extends AsyncTask<String, Void, Boolean> { |
|||
|
|||
private final StoryModel storyModel; |
|||
private final QuestionModel questionModel; |
|||
private final String cookie; |
|||
private final OnTaskCompleteListener onTaskCompleteListener; |
|||
|
|||
public RespondAction(final StoryModel storyModel, |
|||
final QuestionModel questionModel, |
|||
final String cookie, |
|||
final OnTaskCompleteListener onTaskCompleteListener) { |
|||
this.storyModel = storyModel; |
|||
this.questionModel = questionModel; |
|||
this.cookie = cookie; |
|||
this.onTaskCompleteListener = onTaskCompleteListener; |
|||
} |
|||
|
|||
protected Boolean doInBackground(String... rawChoice) { |
|||
final String url = "https://i.instagram.com/api/v1/media/" |
|||
+ storyModel.getStoryMediaId().split("_")[0] + "/" + questionModel.getId() + "/story_question_response/"; |
|||
HttpURLConnection urlConnection = null; |
|||
try { |
|||
final JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString() |
|||
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString() |
|||
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] |
|||
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie) |
|||
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) |
|||
+ "\"}"); |
|||
String choice = rawChoice[0].replaceAll("\"", ("\\\"")); |
|||
ogbody.put("response", choice); |
|||
String urlParameters = Utils.sign(ogbody.toString()); |
|||
urlConnection = (HttpURLConnection) new URL(url).openConnection(); |
|||
urlConnection.setRequestMethod("POST"); |
|||
urlConnection.setUseCaches(false); |
|||
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); |
|||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
|||
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length)); |
|||
urlConnection.setDoOutput(true); |
|||
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); |
|||
wr.writeBytes(urlParameters); |
|||
wr.flush(); |
|||
wr.close(); |
|||
urlConnection.connect(); |
|||
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
return true; |
|||
} |
|||
|
|||
} catch (Throwable ex) { |
|||
Log.e("austin_debug", "respond: " + ex); |
|||
} finally { |
|||
if (urlConnection != null) { |
|||
urlConnection.disconnect(); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final Boolean ok) { |
|||
if (onTaskCompleteListener == null) return; |
|||
onTaskCompleteListener.onTaskComplete(ok); |
|||
|
|||
} |
|||
|
|||
public interface OnTaskCompleteListener { |
|||
void onTaskComplete(final boolean result); |
|||
} |
|||
} |
@ -1,69 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import java.io.DataOutputStream; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
|
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.stickers.PollModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
|
|||
public class VoteAction extends AsyncTask<Integer, Void, Integer> { |
|||
|
|||
private static final String TAG = "VoteAction"; |
|||
|
|||
private final StoryModel storyModel; |
|||
private final PollModel pollModel; |
|||
private final String cookie; |
|||
private final OnTaskCompleteListener onTaskCompleteListener; |
|||
|
|||
public VoteAction(final StoryModel storyModel, |
|||
final PollModel pollModel, |
|||
final String cookie, |
|||
final OnTaskCompleteListener onTaskCompleteListener) { |
|||
this.storyModel = storyModel; |
|||
this.pollModel = pollModel; |
|||
this.cookie = cookie; |
|||
this.onTaskCompleteListener = onTaskCompleteListener; |
|||
} |
|||
|
|||
protected Integer doInBackground(Integer... rawChoice) { |
|||
int choice = rawChoice[0]; |
|||
final String url = "https://www.instagram.com/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + pollModel.getId() + "/story_poll_vote/"; |
|||
try { |
|||
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); |
|||
urlConnection.setRequestMethod("POST"); |
|||
urlConnection.setUseCaches(false); |
|||
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT); |
|||
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); |
|||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
|||
urlConnection.setRequestProperty("Content-Length", "6"); |
|||
urlConnection.setDoOutput(true); |
|||
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); |
|||
wr.writeBytes("vote=" + choice); |
|||
wr.flush(); |
|||
wr.close(); |
|||
urlConnection.connect(); |
|||
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
return choice; |
|||
} |
|||
urlConnection.disconnect(); |
|||
} catch (Exception ex) { |
|||
Log.e(TAG, "Error", ex); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final Integer result) { |
|||
if (result == null || onTaskCompleteListener == null) return; |
|||
onTaskCompleteListener.onTaskComplete(result); |
|||
} |
|||
|
|||
public interface OnTaskCompleteListener { |
|||
void onTaskComplete(final int choice); |
|||
} |
|||
} |
@ -0,0 +1,170 @@ |
|||
package awais.instagrabber.fragments; |
|||
|
|||
import android.content.Context; |
|||
import android.content.res.Resources; |
|||
import android.os.AsyncTask; |
|||
import android.os.Bundle; |
|||
import android.util.Log; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.appcompat.app.AppCompatActivity; |
|||
import androidx.appcompat.widget.LinearLayoutCompat; |
|||
import androidx.navigation.fragment.NavHostFragment; |
|||
import androidx.recyclerview.widget.LinearLayoutManager; |
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|||
|
|||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.adapters.LikesAdapter; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|||
import awais.instagrabber.databinding.FragmentLikesBinding; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
import awais.instagrabber.webservices.GraphQLService; |
|||
import awais.instagrabber.webservices.MediaService; |
|||
import awais.instagrabber.webservices.ServiceCallback; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public final class LikesViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener { |
|||
private static final String TAG = "LikesViewerFragment"; |
|||
|
|||
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); |
|||
|
|||
private LikesAdapter likesAdapter; |
|||
private FragmentLikesBinding binding; |
|||
private LinearLayoutManager layoutManager; |
|||
private Resources resources; |
|||
private AppCompatActivity fragmentActivity; |
|||
private LinearLayoutCompat root; |
|||
private RecyclerLazyLoader lazyLoader; |
|||
private MediaService mediaService; |
|||
private GraphQLService graphQLService; |
|||
private boolean isLoggedIn; |
|||
private String postId, endCursor; |
|||
private boolean isComment; |
|||
|
|||
private final ServiceCallback<List<ProfileModel>> cb = new ServiceCallback<List<ProfileModel>>() { |
|||
@Override |
|||
public void onSuccess(final List<ProfileModel> result) { |
|||
final LikesAdapter likesAdapter = new LikesAdapter(result, v -> { |
|||
final Object tag = v.getTag(); |
|||
if (tag instanceof ProfileModel) { |
|||
ProfileModel model = (ProfileModel) tag; |
|||
final Bundle bundle = new Bundle(); |
|||
bundle.putString("username", "@" + model.getUsername()); |
|||
NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle); |
|||
} |
|||
}); |
|||
binding.rvLikes.setAdapter(likesAdapter); |
|||
binding.rvLikes.setLayoutManager(new LinearLayoutManager(getContext())); |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(final Throwable t) { |
|||
Log.e(TAG, "Error", t); |
|||
try { |
|||
final Context context = getContext(); |
|||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); |
|||
} |
|||
catch (Exception e) {} |
|||
} |
|||
}; |
|||
|
|||
private final ServiceCallback<GraphQLUserListFetchResponse> acb = new ServiceCallback<GraphQLUserListFetchResponse>() { |
|||
@Override |
|||
public void onSuccess(final GraphQLUserListFetchResponse result) { |
|||
endCursor = result.getNextMaxId(); |
|||
final LikesAdapter likesAdapter = new LikesAdapter(result.getItems(), v -> { |
|||
final Object tag = v.getTag(); |
|||
if (tag instanceof ProfileModel) { |
|||
ProfileModel model = (ProfileModel) tag; |
|||
final Bundle bundle = new Bundle(); |
|||
bundle.putString("username", "@" + model.getUsername()); |
|||
NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle); |
|||
} |
|||
}); |
|||
binding.rvLikes.setAdapter(likesAdapter); |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(final Throwable t) { |
|||
Log.e(TAG, "Error", t); |
|||
try { |
|||
final Context context = getContext(); |
|||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); |
|||
} |
|||
catch (Exception e) {} |
|||
} |
|||
}; |
|||
|
|||
@Override |
|||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null; |
|||
fragmentActivity = (AppCompatActivity) getActivity(); |
|||
mediaService = isLoggedIn ? MediaService.getInstance() : null; |
|||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); |
|||
// setHasOptionsMenu(true); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
|||
binding = FragmentLikesBinding.inflate(getLayoutInflater()); |
|||
binding.swipeRefreshLayout.setEnabled(false); |
|||
binding.swipeRefreshLayout.setNestedScrollingEnabled(false); |
|||
root = binding.getRoot(); |
|||
return root; |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
init(); |
|||
} |
|||
|
|||
@Override |
|||
public void onRefresh() { |
|||
if (isComment && !isLoggedIn) { |
|||
lazyLoader.resetState(); |
|||
graphQLService.fetchCommentLikers(postId, null, acb); |
|||
} |
|||
else mediaService.fetchLikes(postId, isComment, cb); |
|||
} |
|||
|
|||
private void init() { |
|||
if (getArguments() == null) return; |
|||
final LikesViewerFragmentArgs fragmentArgs = LikesViewerFragmentArgs.fromBundle(getArguments()); |
|||
postId = fragmentArgs.getPostId(); |
|||
isComment = fragmentArgs.getIsComment(); |
|||
binding.swipeRefreshLayout.setOnRefreshListener(this); |
|||
binding.swipeRefreshLayout.setRefreshing(true); |
|||
resources = getResources(); |
|||
if (isComment && !isLoggedIn) { |
|||
layoutManager = new LinearLayoutManager(getContext()); |
|||
binding.rvLikes.setLayoutManager(layoutManager); |
|||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|||
if (!TextUtils.isEmpty(endCursor)) |
|||
graphQLService.fetchCommentLikers(postId, endCursor, acb); |
|||
endCursor = null; |
|||
}); |
|||
binding.rvLikes.addOnScrollListener(lazyLoader); |
|||
} |
|||
onRefresh(); |
|||
} |
|||
} |
@ -0,0 +1,220 @@ |
|||
package awais.instagrabber.fragments; |
|||
|
|||
import android.content.Context; |
|||
import android.os.Bundle; |
|||
import android.util.Log; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.appcompat.app.ActionBar; |
|||
import androidx.appcompat.app.AppCompatActivity; |
|||
import androidx.fragment.app.Fragment; |
|||
import androidx.lifecycle.ViewModelProvider; |
|||
import androidx.navigation.NavDirections; |
|||
import androidx.navigation.fragment.NavHostFragment; |
|||
import androidx.recyclerview.widget.LinearLayoutManager; |
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.adapters.FeedStoriesListAdapter; |
|||
import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener; |
|||
import awais.instagrabber.adapters.HighlightStoriesListAdapter; |
|||
import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|||
import awais.instagrabber.databinding.FragmentStoryListViewerBinding; |
|||
import awais.instagrabber.fragments.main.FeedFragment; |
|||
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; |
|||
import awais.instagrabber.models.FeedStoryModel; |
|||
import awais.instagrabber.models.HighlightModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.viewmodels.FeedStoriesViewModel; |
|||
import awais.instagrabber.viewmodels.ArchivesViewModel; |
|||
import awais.instagrabber.webservices.ServiceCallback; |
|||
import awais.instagrabber.webservices.StoriesService; |
|||
import awais.instagrabber.webservices.StoriesService.ArchiveFetchResponse; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { |
|||
private static final String TAG = "StoryListViewerFragment"; |
|||
|
|||
private AppCompatActivity fragmentActivity; |
|||
private FragmentStoryListViewerBinding binding; |
|||
private SwipeRefreshLayout root; |
|||
private boolean shouldRefresh = true, firstRefresh = true; |
|||
private FeedStoriesViewModel feedStoriesViewModel; |
|||
private ArchivesViewModel archivesViewModel; |
|||
private StoriesService storiesService; |
|||
private Context context; |
|||
private String type, endCursor = null; |
|||
private RecyclerLazyLoader lazyLoader; |
|||
private FeedStoriesListAdapter adapter; |
|||
|
|||
private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() { |
|||
@Override |
|||
public void onFeedStoryClick(final FeedStoryModel model, final int position) { |
|||
if (model == null) return; |
|||
final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false); |
|||
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); |
|||
} |
|||
|
|||
@Override |
|||
public void onProfileClick(final String username) { |
|||
openProfile(username); |
|||
} |
|||
}; |
|||
|
|||
private final OnHighlightStoryClickListener archiveClickListener = new OnHighlightStoryClickListener() { |
|||
@Override |
|||
public void onHighlightClick(final HighlightModel model, final int position) { |
|||
if (model == null) return; |
|||
final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment( |
|||
position, getString(R.string.action_archive), false, false, null, null, true, false); |
|||
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); |
|||
} |
|||
|
|||
@Override |
|||
public void onProfileClick(final String username) { |
|||
openProfile(username); |
|||
} |
|||
}; |
|||
|
|||
private final ServiceCallback<ArchiveFetchResponse> cb = new ServiceCallback<ArchiveFetchResponse>() { |
|||
@Override |
|||
public void onSuccess(final ArchiveFetchResponse result) { |
|||
endCursor = result.getNextCursor(); |
|||
final List<HighlightModel> models = archivesViewModel.getList().getValue(); |
|||
final List<HighlightModel> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models); |
|||
modelsCopy.addAll(result.getResult()); |
|||
archivesViewModel.getList().postValue(modelsCopy); |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(final Throwable t) { |
|||
Log.e(TAG, "Error", t); |
|||
try { |
|||
final Context context = getContext(); |
|||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); |
|||
} |
|||
catch (Exception e) {} |
|||
} |
|||
}; |
|||
|
|||
@Override |
|||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
fragmentActivity = (AppCompatActivity) requireActivity(); |
|||
context = getContext(); |
|||
if (context == null) return; |
|||
storiesService = StoriesService.getInstance(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
|||
if (root != null) { |
|||
shouldRefresh = false; |
|||
return root; |
|||
} |
|||
binding = FragmentStoryListViewerBinding.inflate(getLayoutInflater()); |
|||
root = binding.getRoot(); |
|||
return root; |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
if (!shouldRefresh) return; |
|||
init(); |
|||
shouldRefresh = false; |
|||
} |
|||
|
|||
@Override |
|||
public void onResume() { |
|||
super.onResume(); |
|||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|||
if (actionBar != null) actionBar.setTitle(type == "feed" ? R.string.feed_stories : R.string.action_archive); |
|||
} |
|||
|
|||
@Override |
|||
public void onDestroy() { |
|||
if (archivesViewModel != null) archivesViewModel.getList().postValue(null); |
|||
super.onDestroy(); |
|||
} |
|||
|
|||
private void init() { |
|||
final Context context = getContext(); |
|||
if (getArguments() == null) return; |
|||
final StoryListViewerFragmentArgs fragmentArgs = StoryListViewerFragmentArgs.fromBundle(getArguments()); |
|||
type = fragmentArgs.getType(); |
|||
binding.swipeRefreshLayout.setOnRefreshListener(this); |
|||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context); |
|||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|||
if (type == "feed") { |
|||
if (actionBar != null) actionBar.setTitle(R.string.feed_stories); |
|||
feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); |
|||
adapter = new FeedStoriesListAdapter(clickListener); |
|||
binding.rvStories.setLayoutManager(layoutManager); |
|||
binding.rvStories.setAdapter(adapter); |
|||
feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); |
|||
} |
|||
else { |
|||
if (actionBar != null) actionBar.setTitle(R.string.action_archive); |
|||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|||
if (!TextUtils.isEmpty(endCursor)) onRefresh(); |
|||
endCursor = null; |
|||
}); |
|||
binding.rvStories.addOnScrollListener(lazyLoader); |
|||
archivesViewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class); |
|||
final HighlightStoriesListAdapter adapter = new HighlightStoriesListAdapter(archiveClickListener); |
|||
binding.rvStories.setLayoutManager(layoutManager); |
|||
binding.rvStories.setAdapter(adapter); |
|||
archivesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); |
|||
} |
|||
onRefresh(); |
|||
} |
|||
|
|||
@Override |
|||
public void onRefresh() { |
|||
binding.swipeRefreshLayout.setRefreshing(true); |
|||
if (type == "feed" && firstRefresh) { |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
adapter.submitList(feedStoriesViewModel.getList().getValue()); |
|||
firstRefresh = false; |
|||
} |
|||
else if (type == "feed") { |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
storiesService.getFeedStories(CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<List<FeedStoryModel>>() { |
|||
@Override |
|||
public void onSuccess(final List<FeedStoryModel> result) { |
|||
feedStoriesViewModel.getList().postValue(result); |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(final Throwable t) { |
|||
Log.e(TAG, "failed", t); |
|||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); |
|||
} |
|||
}); |
|||
} |
|||
else if (type == "archive") { |
|||
storiesService.fetchArchive(endCursor, cb); |
|||
} |
|||
} |
|||
|
|||
private void openProfile(final String username) { |
|||
final NavDirections action = MorePreferencesFragmentDirections |
|||
.actionGlobalProfileFragment("@" + username); |
|||
NavHostFragment.findNavController(this).navigate(action); |
|||
} |
|||
} |
@ -1,36 +1,66 @@ |
|||
package awais.instagrabber.models; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
|
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public final class FeedStoryModel implements Serializable { |
|||
private final String storyMediaId; |
|||
private final ProfileModel profileModel; |
|||
private StoryModel[] storyModels; |
|||
private boolean fullyRead; |
|||
private final StoryModel firstStoryModel; |
|||
private Boolean fullyRead; |
|||
private final long timestamp; |
|||
private final int mediaCount; |
|||
|
|||
public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead) { |
|||
public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead, |
|||
final long timestamp, final StoryModel firstStoryModel, final int mediaCount) { |
|||
this.storyMediaId = storyMediaId; |
|||
this.profileModel = profileModel; |
|||
this.fullyRead = fullyRead; |
|||
this.timestamp = timestamp; |
|||
this.firstStoryModel = firstStoryModel; |
|||
this.mediaCount = mediaCount; |
|||
} |
|||
|
|||
public String getStoryMediaId() { |
|||
return storyMediaId; |
|||
} |
|||
|
|||
public long getTimestamp() { |
|||
return timestamp; |
|||
} |
|||
|
|||
@NonNull |
|||
public String getDateTime() { |
|||
return Utils.datetimeParser.format(new Date(timestamp * 1000L)); |
|||
} |
|||
|
|||
public int getMediaCount() { |
|||
return mediaCount; |
|||
} |
|||
|
|||
public ProfileModel getProfileModel() { |
|||
return profileModel; |
|||
} |
|||
|
|||
public void setStoryModels(final StoryModel[] storyModels) { |
|||
this.storyModels = storyModels; |
|||
} |
|||
// public void setFirstStoryModel(final StoryModel firstStoryModel) { |
|||
// this.firstStoryModel = firstStoryModel; |
|||
// } |
|||
|
|||
public StoryModel[] getStoryModels() { |
|||
return storyModels; |
|||
public StoryModel getFirstStoryModel() { |
|||
return firstStoryModel; |
|||
} |
|||
|
|||
public boolean getFullyRead() { |
|||
public Boolean isFullyRead() { |
|||
return fullyRead; |
|||
} |
|||
|
|||
public void setFullyRead(final boolean fullyRead) { |
|||
this.fullyRead = fullyRead; |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
package awais.instagrabber.models.stickers; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public final class SliderModel implements Serializable { |
|||
private final int voteCount; |
|||
private final Double average; |
|||
private Double myChoice; |
|||
private final boolean canVote; |
|||
private final String id, question, emoji; |
|||
|
|||
public SliderModel(final String id, final String question, final String emoji, final boolean canVote, |
|||
final Double average, final int voteCount, final Double myChoice) { |
|||
this.id = id; |
|||
this.question = question; |
|||
this.emoji = emoji; |
|||
this.canVote = canVote; |
|||
this.average = average; |
|||
this.voteCount = voteCount; |
|||
this.myChoice = myChoice; |
|||
} |
|||
|
|||
public String getId() { |
|||
return id; |
|||
} |
|||
|
|||
public String getQuestion() { |
|||
return question; |
|||
} |
|||
|
|||
public String getEmoji() { |
|||
return emoji; |
|||
} |
|||
|
|||
public boolean canVote() { |
|||
return canVote; |
|||
} |
|||
|
|||
public int getVoteCount() { |
|||
return voteCount; |
|||
} |
|||
|
|||
public Double getAverage() { |
|||
return average; |
|||
} |
|||
|
|||
public Double getMyChoice() { return myChoice; } |
|||
|
|||
public Double setMyChoice(final Double choice) { |
|||
this.myChoice = choice; |
|||
return choice; |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
package awais.instagrabber.repositories; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import retrofit2.Call; |
|||
import retrofit2.http.GET; |
|||
import retrofit2.http.QueryMap; |
|||
|
|||
public interface GraphQLRepository { |
|||
@GET("/graphql/query/") |
|||
Call<String> fetch(@QueryMap(encoded = true) Map<String, String> queryParams); |
|||
} |
@ -0,0 +1,79 @@ |
|||
package awais.instagrabber.repositories.responses; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
|
|||
public class GraphQLUserListFetchResponse { |
|||
private String nextMaxId; |
|||
private String status; |
|||
private List<ProfileModel> items; |
|||
|
|||
public GraphQLUserListFetchResponse(final String nextMaxId, |
|||
final String status, |
|||
final List<ProfileModel> items) { |
|||
this.nextMaxId = nextMaxId; |
|||
this.status = status; |
|||
this.items = items; |
|||
} |
|||
|
|||
public boolean isMoreAvailable() { |
|||
return !TextUtils.isEmpty(nextMaxId); |
|||
} |
|||
|
|||
public String getNextMaxId() { |
|||
return nextMaxId; |
|||
} |
|||
|
|||
public GraphQLUserListFetchResponse setNextMaxId(final String nextMaxId) { |
|||
this.nextMaxId = nextMaxId; |
|||
return this; |
|||
} |
|||
|
|||
public String getStatus() { |
|||
return status; |
|||
} |
|||
|
|||
public GraphQLUserListFetchResponse setStatus(final String status) { |
|||
this.status = status; |
|||
return this; |
|||
} |
|||
|
|||
public List<ProfileModel> getItems() { |
|||
return items; |
|||
} |
|||
|
|||
public GraphQLUserListFetchResponse setItems(final List<ProfileModel> items) { |
|||
this.items = items; |
|||
return this; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(final Object o) { |
|||
if (this == o) return true; |
|||
if (o == null || getClass() != o.getClass()) return false; |
|||
final GraphQLUserListFetchResponse that = (GraphQLUserListFetchResponse) o; |
|||
return Objects.equals(nextMaxId, that.nextMaxId) && |
|||
Objects.equals(status, that.status) && |
|||
Objects.equals(items, that.items); |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
return Objects.hash(nextMaxId, status, items); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public String toString() { |
|||
return "GraphQLUserListFetchResponse{" + |
|||
"nextMaxId='" + nextMaxId + '\'' + |
|||
", status='" + status + '\'' + |
|||
", items=" + items + |
|||
'}'; |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
package awais.instagrabber.repositories.responses; |
|||
|
|||
public class StoryStickerResponse { |
|||
private final String status; |
|||
|
|||
public StoryStickerResponse(final String status) { |
|||
this.status = status; |
|||
} |
|||
|
|||
public String getStatus() { |
|||
return status; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return "StoryStickerResponse{" + |
|||
"status='" + status + '\'' + |
|||
'}'; |
|||
} |
|||
} |
@ -8,6 +8,7 @@ public final class Constants { |
|||
public static final String CUSTOM_DATE_TIME_FORMAT = "date_time_custom_format"; |
|||
public static final String APP_THEME = "app_theme_v19"; |
|||
public static final String APP_LANGUAGE = "app_language_v19"; |
|||
public static final String STORY_SORT = "story_sort"; |
|||
// int prefs |
|||
public static final String PREV_INSTALL_VERSION = "prevVersion"; |
|||
// boolean prefs |
|||
@ -56,9 +57,9 @@ public final class Constants { |
|||
// spoof |
|||
public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " + |
|||
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 " + |
|||
"Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)"; |
|||
"Instagram 169.1.0.29.135 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 262886998)"; |
|||
public static final String I_USER_AGENT = |
|||
"Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)"; |
|||
"Instagram 169.1.0.29.135 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 262886998)"; |
|||
public static final String A_USER_AGENT = "https://Barinsta.AustinHuang.me / mailto:[email protected]"; |
|||
// see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts |
|||
public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" + |
|||
|
@ -0,0 +1,19 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.models.HighlightModel; |
|||
|
|||
public class ArchivesViewModel extends ViewModel { |
|||
private MutableLiveData<List<HighlightModel>> list; |
|||
|
|||
public MutableLiveData<List<HighlightModel>> getList() { |
|||
if (list == null) { |
|||
list = new MutableLiveData<>(); |
|||
} |
|||
return list; |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.models.FollowModel; |
|||
|
|||
public class FollowViewModel extends ViewModel { |
|||
private MutableLiveData<List<FollowModel>> list; |
|||
|
|||
public MutableLiveData<List<FollowModel>> getList() { |
|||
if (list == null) { |
|||
list = new MutableLiveData<>(); |
|||
} |
|||
return list; |
|||
} |
|||
} |
@ -0,0 +1,243 @@ |
|||
package awais.instagrabber.webservices; |
|||
|
|||
import android.os.Handler; |
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONException; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.io.InputStreamReader; |
|||
import java.io.Reader; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
import awais.instagrabber.models.FeedModel; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.repositories.GraphQLRepository; |
|||
import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; |
|||
import awais.instagrabber.repositories.responses.PostsFetchResponse; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.ResponseBodyUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
import retrofit2.Retrofit; |
|||
|
|||
public class GraphQLService extends BaseService { |
|||
private static final String TAG = "GraphQLService"; |
|||
private static final boolean loadFromMock = false; |
|||
|
|||
private final GraphQLRepository repository; |
|||
|
|||
private static GraphQLService instance; |
|||
|
|||
private GraphQLService() { |
|||
final Retrofit retrofit = getRetrofitBuilder() |
|||
.baseUrl("https://www.instagram.com") |
|||
.build(); |
|||
repository = retrofit.create(GraphQLRepository.class); |
|||
} |
|||
|
|||
public static GraphQLService getInstance() { |
|||
if (instance == null) { |
|||
instance = new GraphQLService(); |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
private void fetch(final String queryHash, |
|||
final String variables, |
|||
final String arg1, |
|||
final String arg2, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
final Map<String, String> queryMap = new HashMap<>(); |
|||
queryMap.put("query_hash", queryHash); |
|||
queryMap.put("variables", variables); |
|||
final Call<String> request = repository.fetch(queryMap); |
|||
request.enqueue(new Callback<String>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { |
|||
try { |
|||
// Log.d(TAG, "onResponse: body: " + response.body()); |
|||
final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2); |
|||
if (callback != null) { |
|||
callback.onSuccess(postsFetchResponse); |
|||
} |
|||
} catch (JSONException e) { |
|||
Log.e(TAG, "onResponse", e); |
|||
if (callback != null) { |
|||
callback.onFailure(e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchLocationPosts(@NonNull final String locationId, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
fetch("36bd0f2bf5911908de389b8ceaa3be6d", |
|||
"{\"id\":\"" + locationId + "\"," + |
|||
"\"first\":25," + |
|||
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", |
|||
Constants.EXTRAS_LOCATION, |
|||
"edge_location_to_media", |
|||
callback); |
|||
} |
|||
|
|||
public void fetchHashtagPosts(@NonNull final String tag, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
fetch("9b498c08113f1e09617a1703c22b2f32", |
|||
"{\"tag_name\":\"" + tag + "\"," + |
|||
"\"first\":25," + |
|||
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", |
|||
Constants.EXTRAS_HASHTAG, |
|||
"edge_hashtag_to_media", |
|||
callback); |
|||
} |
|||
|
|||
public void fetchProfilePosts(@NonNull final String profileId, |
|||
final int postsPerPage, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
fetch("18a7b935ab438c4514b1f742d8fa07a7", |
|||
"{\"id\":\"" + profileId + "\"," + |
|||
"\"first\":" + postsPerPage + "," + |
|||
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", |
|||
Constants.EXTRAS_USER, |
|||
"edge_owner_to_timeline_media", |
|||
callback); |
|||
} |
|||
|
|||
public void fetchTaggedPosts(@NonNull final String profileId, |
|||
final int postsPerPage, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
fetch("31fe64d9463cbbe58319dced405c6206", |
|||
"{\"id\":\"" + profileId + "\"," + |
|||
"\"first\":" + postsPerPage + "," + |
|||
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", |
|||
Constants.EXTRAS_USER, |
|||
"edge_user_to_photos_of_you", |
|||
callback); |
|||
} |
|||
|
|||
@NonNull |
|||
private PostsFetchResponse parsePostResponse(@NonNull final Response<String> response, @NonNull final String arg1, @NonNull final String arg2) throws JSONException { |
|||
if (TextUtils.isEmpty(response.body())) { |
|||
Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code()); |
|||
return new PostsFetchResponse(Collections.emptyList(), false, null); |
|||
} |
|||
return parseResponseBody(response.body(), arg1, arg2); |
|||
} |
|||
|
|||
@NonNull |
|||
private PostsFetchResponse parseResponseBody(@NonNull final String body, @NonNull final String arg1, @NonNull final String arg2) |
|||
throws JSONException { |
|||
final List<FeedModel> feedModels = new ArrayList<>(); |
|||
final JSONObject timelineFeed = new JSONObject(body) |
|||
.getJSONObject("data") |
|||
.getJSONObject(arg1) |
|||
.getJSONObject(arg2); |
|||
final String endCursor; |
|||
final boolean hasNextPage; |
|||
|
|||
final JSONObject pageInfo = timelineFeed.getJSONObject("page_info"); |
|||
if (pageInfo.has("has_next_page")) { |
|||
hasNextPage = pageInfo.getBoolean("has_next_page"); |
|||
endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null; |
|||
} else { |
|||
hasNextPage = false; |
|||
endCursor = null; |
|||
} |
|||
|
|||
final JSONArray feedItems = timelineFeed.getJSONArray("edges"); |
|||
|
|||
for (int i = 0; i < feedItems.length(); ++i) { |
|||
final JSONObject itemJson = feedItems.optJSONObject(i); |
|||
if (itemJson == null) { |
|||
continue; |
|||
} |
|||
final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); |
|||
if (feedModel != null) { |
|||
feedModels.add(feedModel); |
|||
} |
|||
} |
|||
return new PostsFetchResponse(feedModels, hasNextPage, endCursor); |
|||
} |
|||
|
|||
public void fetchCommentLikers(final String commentId, |
|||
final String endCursor, |
|||
final ServiceCallback<GraphQLUserListFetchResponse> callback) { |
|||
final Map<String, String> queryMap = new HashMap<>(); |
|||
queryMap.put("query_hash", "5f0b1f6281e72053cbc07909c8d154ae"); |
|||
queryMap.put("variables", "{\"comment_id\":\"" + commentId + "\"," + |
|||
"\"first\":30," + |
|||
"\"after\":\"" + (endCursor == null ? "" : endCursor) + "\"}"); |
|||
final Call<String> request = repository.fetch(queryMap); |
|||
request.enqueue(new Callback<String>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { |
|||
final String rawBody = response.body(); |
|||
if (rawBody == null) { |
|||
Log.e(TAG, "Error occurred while fetching gql comment likes of "+commentId); |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
try { |
|||
final JSONObject body = new JSONObject(rawBody); |
|||
final String status = body.getString("status"); |
|||
final JSONObject data = body.getJSONObject("data").getJSONObject("comment").getJSONObject("edge_liked_by"); |
|||
final JSONObject pageInfo = data.getJSONObject("page_info"); |
|||
final String endCursor = pageInfo.getBoolean("has_next_page") ? pageInfo.getString("end_cursor") : null; |
|||
final JSONArray users = data.getJSONArray("edges"); |
|||
final int usersLen = users.length(); |
|||
final List<ProfileModel> userModels = new ArrayList<>(); |
|||
for (int j = 0; j < usersLen; ++j) { |
|||
final JSONObject userObject = users.getJSONObject(j).getJSONObject("node"); |
|||
userModels.add(new ProfileModel(userObject.optBoolean("is_private"), |
|||
false, |
|||
userObject.optBoolean("is_verified"), |
|||
userObject.getString("id"), |
|||
userObject.getString("username"), |
|||
userObject.optString("full_name"), |
|||
null, null, |
|||
userObject.getString("profile_pic_url"), |
|||
null, 0, 0, 0, false, false, false, false, false)); |
|||
} |
|||
callback.onSuccess(new GraphQLUserListFetchResponse(endCursor, status, userModels)); |
|||
} catch (JSONException e) { |
|||
Log.e(TAG, "onResponse", e); |
|||
if (callback != null) { |
|||
callback.onFailure(e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue