mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-23 13:26:59 +00:00
Merge branch 'master' into dm-notifications-enhancements
This commit is contained in:
commit
d42d4dd3ea
@ -61,6 +61,15 @@
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CrazyMarvin",
|
||||
"name": "CrazyMarvin",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/15004217?v=4",
|
||||
"profile": "https://github.com/CrazyMarvin",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "KevinNThomas",
|
||||
"name": "Kevin Thomas",
|
||||
@ -82,16 +91,6 @@
|
||||
"question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "e-edgren",
|
||||
"name": "Airikr",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
|
||||
"profile": "https://airikr.me/",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alin-1",
|
||||
"name": "ALIN",
|
||||
@ -102,6 +101,26 @@
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RickyM7",
|
||||
"name": "Ricardo",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/24703825?v=4",
|
||||
"profile": "https://github.com/RickyM7",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "e-edgren",
|
||||
"name": "Airikr",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
|
||||
"profile": "https://airikr.me/",
|
||||
"contributions": [
|
||||
"ideas",
|
||||
"question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Akrai",
|
||||
"name": "Akrai",
|
||||
@ -112,6 +131,15 @@
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cizordj",
|
||||
"name": "Cézar Augusto",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/32869222?v=4",
|
||||
"profile": "https://github.com/cizordj",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "farzadx",
|
||||
"name": "farzadx",
|
||||
@ -149,19 +177,28 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kernoeb",
|
||||
"name": "kernoeb",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/24623168",
|
||||
"profile": "https://becauseofprog.fr/",
|
||||
"login": "initdebugs",
|
||||
"name": "Initdebugs",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/75781464?v=4",
|
||||
"profile": "https://github.com/initdebugs",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Lego8486",
|
||||
"name": "Ten_Lego",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/47414485",
|
||||
"profile": "https://github.com/Lego8486",
|
||||
"login": "CrafterSvK",
|
||||
"name": "Jakub Janek",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8365659?v=4",
|
||||
"profile": "https://janek.xyz/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kernoeb",
|
||||
"name": "kernoeb",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/24623168",
|
||||
"profile": "https://becauseofprog.fr/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
@ -229,16 +266,6 @@
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RickyM7",
|
||||
"name": "Ricardo",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/24703825?v=4",
|
||||
"profile": "https://github.com/RickyM7",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rikishi0071",
|
||||
"name": "rikishi0071",
|
||||
@ -257,6 +284,15 @@
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Lego8486",
|
||||
"name": "Ten_Lego",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/47414485",
|
||||
"profile": "https://github.com/Lego8486",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wagnim",
|
||||
"name": "wagnim",
|
||||
@ -265,6 +301,15 @@
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ysakamoto",
|
||||
"name": "ysakamoto",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1331642?v=4",
|
||||
"profile": "https://github.com/ysakamoto",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 6,
|
||||
|
46
README.md
46
README.md
@ -9,7 +9,7 @@
|
||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
|
||||
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-27-orange.svg)](#contributors)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-32-orange.svg)](#contributors)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
We're previously known as InstaGrabber.
|
||||
@ -32,11 +32,15 @@ Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.insta
|
||||
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg" alt="Hashtag" width="15%"/></a>
|
||||
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/6.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/6.jpg" alt="Discover Topics" width="15%"/></a>
|
||||
|
||||
## We need maintainers!
|
||||
|
||||
To speed up development, we need more hands on deck. If you are proficient in Java and Android development, and are willing to perform such a public service, please [contact us](https://t.me/austinhuang).
|
||||
|
||||
## Contact us
|
||||
|
||||
* Use [GitHub issues](https://github.com/austinhuang0131/instagrabber/issues) when possible.
|
||||
* Email: [Barinsta@AustinHuang.me](mailto:barinsta@austinhuang.me?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: [![r/Barinsta](https://img.shields.io/reddit/subreddit-subscribers/Barinsta?style=social)](https://reddit.com/r/barinsta)
|
||||
* Chat (Bridged to each other): [![Matrix](https://img.shields.io/badge/Matrix-%23Barinsta:matrix.org-000000?logo=matrix)](https://matrix.to/#/#barinsta:matrix.org) [![Telegram](https://img.shields.io/badge/Telegram-@Grabber__App-2CA5E0?logo=telegram)](https://t.me/grabber_app) [![Discord](https://img.shields.io/badge/Discord-YtEDzN2-7289da?logo=discord&logoColor=white)](https://discord.gg/YtEDzN2) [![Gitter](https://img.shields.io/badge/Gitter-InstaGrabber/General-ed1965?logo=gitter)](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>
|
||||
</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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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://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>
|
||||
<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/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>
|
||||
<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,12 +103,11 @@ 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>
|
||||
Ammar Githam <ammargitham786@gmail.com>
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -10,8 +10,8 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
|
||||
versionCode 54
|
||||
versionName '19.0.2'
|
||||
versionCode 56
|
||||
versionName '19.0.4'
|
||||
|
||||
multiDexEnabled true
|
||||
|
||||
@ -98,6 +98,9 @@ dependencies {
|
||||
implementation "androidx.emoji:emoji:$emoji_compat_version"
|
||||
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
|
||||
|
||||
// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
|
||||
implementation 'me.austinhuang:AutoLinkTextViewV2:-SNAPSHOT'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
implementation 'com.facebook.fresco:fresco:2.3.0'
|
||||
implementation 'com.facebook.fresco:animated-webp:2.3.0'
|
||||
@ -109,7 +112,6 @@ dependencies {
|
||||
|
||||
implementation 'org.apache.commons:commons-imaging:1.0-alpha2'
|
||||
implementation 'com.ibm.icu:icu4j:68.1'
|
||||
implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT'
|
||||
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
|
||||
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'
|
||||
|
||||
|
@ -83,8 +83,8 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
|
||||
}
|
||||
};
|
||||
private final CommentCallback commentCallback;
|
||||
private CommentModel selected;
|
||||
private int selectedIndex;
|
||||
private CommentModel selected, toChangeLike;
|
||||
private int selectedIndex, likedIndex;
|
||||
|
||||
public CommentsAdapter(final CommentCallback commentCallback) {
|
||||
super(DIFF_CALLBACK);
|
||||
@ -106,10 +106,12 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
|
||||
final CommentModel commentModel = getItem(position);
|
||||
CommentModel commentModel = getItem(position);
|
||||
if (commentModel == null) return;
|
||||
final int type = getItemViewType(position);
|
||||
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
|
||||
final boolean toLike = this.toChangeLike != null && this.toChangeLike.getId().equals(commentModel.getId());
|
||||
if (toLike) commentModel = this.toChangeLike;
|
||||
if (type == TYPE_PARENT) {
|
||||
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
|
||||
viewHolder.bind(commentModel, selected, commentCallback);
|
||||
@ -174,6 +176,14 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
|
||||
notifyItemChanged(selectedIndex);
|
||||
}
|
||||
|
||||
public void setLiked(final CommentModel commentModel, final boolean liked) {
|
||||
likedIndex = getCurrentList().indexOf(commentModel);
|
||||
CommentModel newCommentModel = commentModel;
|
||||
newCommentModel.setLiked(liked);
|
||||
this.toChangeLike = newCommentModel;
|
||||
notifyItemChanged(likedIndex);
|
||||
}
|
||||
|
||||
public CommentModel getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
||||
return oldItem.getPostId().equals(newItem.getPostId());
|
||||
return oldItem.getPostId().equals(newItem.getPostId()) && oldItem.getPostCaption().equals(newItem.getPostCaption());
|
||||
}
|
||||
};
|
||||
private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {
|
||||
|
@ -4,12 +4,19 @@ 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.FeedStoryViewHolder;
|
||||
import awais.instagrabber.databinding.ItemHighlightBinding;
|
||||
import awais.instagrabber.models.FeedStoryModel;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedStoryViewHolder> {
|
||||
private final OnFeedStoryClickListener listener;
|
||||
@ -22,7 +29,7 @@ public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedSt
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
|
||||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
|
||||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead());
|
||||
}
|
||||
};
|
||||
|
||||
@ -47,5 +54,7 @@ public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedSt
|
||||
|
||||
public interface OnFeedStoryClickListener {
|
||||
void onFeedStoryClick(FeedStoryModel model, int position);
|
||||
|
||||
void onFeedStoryLongClick(FeedStoryModel model, int position);
|
||||
}
|
||||
}
|
||||
|
59
app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java
Executable file
59
app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java
Executable file
@ -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);
|
||||
}
|
||||
}
|
45
app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
Executable file
45
app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
Executable file
@ -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();
|
||||
}
|
||||
}
|
@ -76,16 +76,23 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
private List<NotificationModel> sort(final List<NotificationModel> list) {
|
||||
final List<NotificationModel> listCopy = new ArrayList<>(list);
|
||||
Collections.sort(listCopy, (o1, o2) -> {
|
||||
if (o1.getType() == o2.getType()) return 0;
|
||||
// keep requests at top
|
||||
if (o1.getType() == NotificationType.REQUEST) return -1;
|
||||
if (o2.getType() == NotificationType.REQUEST) return 1;
|
||||
return 0;
|
||||
if (o1.getType() == o2.getType()
|
||||
&& o1.getType() == NotificationType.REQUEST
|
||||
&& o2.getType() == NotificationType.REQUEST) return 0;
|
||||
else if (o1.getType() == NotificationType.REQUEST) return -1;
|
||||
else if (o2.getType() == NotificationType.REQUEST) return 1;
|
||||
// timestamp
|
||||
return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
|
||||
});
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
public interface OnNotificationClickListener {
|
||||
void onNotificationClick(final NotificationModel model);
|
||||
|
||||
void onProfileClick(final String username);
|
||||
|
||||
void onPreviewClick(final NotificationModel model);
|
||||
}
|
||||
}
|
@ -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);
|
||||
// }
|
||||
// }
|
@ -24,10 +24,14 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
|
||||
if (listener == null) return;
|
||||
listener.onFeedStoryClick(model, position);
|
||||
});
|
||||
binding.getRoot().setOnLongClickListener(v -> {
|
||||
if (listener != null) listener.onFeedStoryLongClick(model, position);
|
||||
return true;
|
||||
});
|
||||
final ProfileModel profileModel = model.getProfileModel();
|
||||
binding.title.setText(profileModel.getUsername());
|
||||
binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
|
||||
binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
|
||||
binding.icon.setImageURI(profileModel.getSdProfilePic());
|
||||
binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
|
||||
binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -24,10 +24,6 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
||||
public void bind(final NotificationModel model,
|
||||
final OnNotificationClickListener notificationClickListener) {
|
||||
if (model == null) return;
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onNotificationClick(model);
|
||||
});
|
||||
int text = -1;
|
||||
CharSequence subtext = null;
|
||||
switch (model.getType()) {
|
||||
@ -52,20 +48,54 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
||||
text = R.string.request_notif;
|
||||
subtext = model.getText();
|
||||
break;
|
||||
case COMMENT_LIKE:
|
||||
case TAGGED_COMMENT:
|
||||
case RESPONDED_STORY:
|
||||
subtext = model.getText();
|
||||
break;
|
||||
case AYML:
|
||||
subtext = model.getPostId();
|
||||
break;
|
||||
}
|
||||
binding.tvUsername.setText(model.getUsername());
|
||||
binding.tvComment.setText(text);
|
||||
binding.tvSubComment.setText(subtext, subtext instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
|
||||
// binding.tvSubComment.setMentionClickListener(mentionClickListener);
|
||||
if (model.getType() != NotificationType.REQUEST) {
|
||||
binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext);
|
||||
if (text == -1 && subtext != null) {
|
||||
binding.tvComment.setText(subtext);
|
||||
binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE);
|
||||
binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
else if (text != -1) {
|
||||
binding.tvComment.setText(text);
|
||||
binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
if (model.getType() != NotificationType.REQUEST && model.getType() != NotificationType.AYML) {
|
||||
binding.tvDate.setText(model.getDateTime());
|
||||
}
|
||||
|
||||
binding.tvUsername.setText(model.getUsername());
|
||||
binding.ivProfilePic.setImageURI(model.getProfilePic());
|
||||
if (TextUtils.isEmpty(model.getPreviewPic())) {
|
||||
binding.ivProfilePic.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onProfileClick(model.getUsername());
|
||||
});
|
||||
|
||||
if (model.getType() == NotificationType.AYML) {
|
||||
binding.ivPreviewPic.setVisibility(View.GONE);
|
||||
}
|
||||
else if (TextUtils.isEmpty(model.getPreviewPic())) {
|
||||
binding.ivPreviewPic.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
binding.ivPreviewPic.setVisibility(View.VISIBLE);
|
||||
binding.ivPreviewPic.setImageURI(model.getPreviewPic());
|
||||
binding.ivPreviewPic.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onPreviewClick(model);
|
||||
});
|
||||
}
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onNotificationClick(model);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -89,8 +89,7 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final void setLiked(final boolean liked) {
|
||||
if (liked) {
|
||||
// container.setBackgroundColor(0x40FF69B4);
|
||||
return;
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
|
||||
}
|
||||
}
|
||||
}
|
@ -89,8 +89,7 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final void setLiked(final boolean liked) {
|
||||
if (liked) {
|
||||
// container.setBackgroundColor(0x40FF69B4);
|
||||
return;
|
||||
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
|
||||
}
|
||||
}
|
||||
}
|
@ -28,11 +28,12 @@ import static awais.instagrabber.utils.Utils.logCollector;
|
||||
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
|
||||
private static final String TAG = "CommentsFetcher";
|
||||
|
||||
private final String shortCode;
|
||||
private final String shortCode, endCursor;
|
||||
private final FetchListener<List<CommentModel>> fetchListener;
|
||||
|
||||
public CommentsFetcher(final String shortCode, final FetchListener<List<CommentModel>> fetchListener) {
|
||||
public CommentsFetcher(final String shortCode, final String endCursor, final FetchListener<List<CommentModel>> fetchListener) {
|
||||
this.shortCode = shortCode;
|
||||
this.endCursor = endCursor;
|
||||
this.fetchListener = fetchListener;
|
||||
}
|
||||
|
||||
@ -48,15 +49,17 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
|
||||
*/
|
||||
final List<CommentModel> commentModels = getParentComments();
|
||||
for (final CommentModel commentModel : commentModels) {
|
||||
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||
if (childCommentModels != null) {
|
||||
final int childCommentsLen = childCommentModels.size();
|
||||
final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
|
||||
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
|
||||
final List<CommentModel> remoteChildComments = getChildComments(commentModel.getId());
|
||||
commentModel.setChildCommentModels(remoteChildComments);
|
||||
lastChild.setPageCursor(false, null);
|
||||
if (commentModels != null) {
|
||||
for (final CommentModel commentModel : commentModels) {
|
||||
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||
if (childCommentModels != null) {
|
||||
final int childCommentsLen = childCommentModels.size();
|
||||
final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
|
||||
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
|
||||
final List<CommentModel> remoteChildComments = getChildComments(commentModel.getId());
|
||||
commentModel.setChildCommentModels(remoteChildComments);
|
||||
lastChild.setPageCursor(false, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,11 +79,10 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
@NonNull
|
||||
private synchronized List<CommentModel> getChildComments(final String commentId) {
|
||||
final List<CommentModel> commentModels = new ArrayList<>();
|
||||
String endCursor = "";
|
||||
while (endCursor != null) {
|
||||
String childEndCursor = "";
|
||||
while (childEndCursor != null) {
|
||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
|
||||
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
|
||||
|
||||
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + childEndCursor + "\"}";
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
@ -93,8 +95,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
.getJSONObject("edge_threaded_comments");
|
||||
|
||||
final JSONObject pageInfo = data.getJSONObject("page_info");
|
||||
endCursor = pageInfo.getString("end_cursor");
|
||||
if (TextUtils.isEmpty(endCursor)) endCursor = null;
|
||||
childEndCursor = pageInfo.getString("end_cursor");
|
||||
if (TextUtils.isEmpty(childEndCursor)) childEndCursor = null;
|
||||
|
||||
final JSONArray childComments = data.optJSONArray("edges");
|
||||
if (childComments != null) {
|
||||
@ -120,6 +122,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
|
||||
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
|
||||
@ -142,6 +145,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
"getChildComments",
|
||||
new Pair<>("commentModels.size", commentModels.size()));
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
if (fetchListener != null) fetchListener.onFailure(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -152,17 +156,14 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
@NonNull
|
||||
private synchronized List<CommentModel> getParentComments() {
|
||||
final List<CommentModel> commentModels = new ArrayList<>();
|
||||
String endCursor = "";
|
||||
while (endCursor != null) {
|
||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
|
||||
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
|
||||
|
||||
try {
|
||||
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
|
||||
else {
|
||||
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
||||
.getJSONObject("shortcode_media")
|
||||
@ -170,8 +171,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
"edge_media_to_parent_comment");
|
||||
|
||||
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
|
||||
endCursor = pageInfo.optString("end_cursor");
|
||||
if (TextUtils.isEmpty(endCursor)) endCursor = null;
|
||||
final String foundEndCursor = pageInfo.optString("end_cursor");
|
||||
final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor));
|
||||
|
||||
// final boolean containsToken = endCursor.contains("bifilter_token");
|
||||
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
|
||||
@ -209,6 +210,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
|
||||
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
|
||||
@ -219,6 +221,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||
comment.getBoolean("viewer_has_liked"),
|
||||
profileModel);
|
||||
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
|
||||
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
|
||||
JSONObject tempJsonObject;
|
||||
final JSONArray childCommentsArray;
|
||||
final int childCommentsLen;
|
||||
@ -227,13 +231,13 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
&& (childCommentsLen = childCommentsArray.length()) > 0) {
|
||||
|
||||
final String childEndCursor;
|
||||
final boolean hasNextPage;
|
||||
final boolean childHasNextPage;
|
||||
if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
|
||||
childEndCursor = tempJsonObject.optString("end_cursor");
|
||||
hasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
|
||||
childHasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
|
||||
} else {
|
||||
childEndCursor = null;
|
||||
hasNextPage = false;
|
||||
childHasNextPage = false;
|
||||
}
|
||||
|
||||
final List<CommentModel> childCommentModels = new ArrayList<>();
|
||||
@ -257,6 +261,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
|
||||
tempJsonObject = childComment.optJSONObject("edge_liked_by");
|
||||
@ -267,7 +272,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
childComment.getBoolean("viewer_has_liked"),
|
||||
childProfileModel));
|
||||
}
|
||||
childCommentModels.get(childCommentsLen - 1).setPageCursor(hasNextPage, childEndCursor);
|
||||
childCommentModels.get(childCommentsLen - 1).setPageCursor(childHasNextPage, childEndCursor);
|
||||
commentModel.setChildCommentModels(childCommentModels);
|
||||
}
|
||||
commentModels.add(commentModel);
|
||||
@ -280,9 +285,9 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
|
||||
new Pair<>("commentModelsList.size", commentModels.size()));
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
break;
|
||||
if (fetchListener != null) fetchListener.onFailure(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return commentModels;
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,61 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
//import android.os.Handler;
|
||||
//import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.webservices.FeedService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
||||
private static final String TAG = "FeedPostFetchService";
|
||||
private final FeedService feedService;
|
||||
private String nextCursor;
|
||||
// private final Handler handler;
|
||||
private boolean hasNextPage;
|
||||
// private static final int DELAY_MILLIS = 500;
|
||||
|
||||
public FeedPostFetchService() {
|
||||
feedService = FeedService.getInstance();
|
||||
// handler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
feedService.fetch(25, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
final List<FeedModel> feedModels = new ArrayList<>();
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
feedModels.clear();
|
||||
feedService.fetch(csrfToken, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
if (result == null && feedModels.size() > 0) {
|
||||
fetchListener.onResult(feedModels);
|
||||
return;
|
||||
}
|
||||
else if (result == null) return;
|
||||
nextCursor = result.getNextCursor();
|
||||
hasNextPage = result.hasNextPage();
|
||||
feedModels.addAll(result.getFeedModels());
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getFeedModels());
|
||||
if (feedModels.size() < 15 && hasNextPage) {
|
||||
// handler.postDelayed(() -> {
|
||||
feedService.fetch(csrfToken, nextCursor, this);
|
||||
// }, DELAY_MILLIS);
|
||||
}
|
||||
else {
|
||||
fetchListener.onResult(feedModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -16,12 +16,21 @@ import awais.instagrabber.utils.TextUtils;
|
||||
public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> {
|
||||
private static final String TAG = "GetActivityAsyncTask";
|
||||
|
||||
private OnTaskCompleteListener onTaskCompleteListener;
|
||||
private final OnTaskCompleteListener onTaskCompleteListener;
|
||||
|
||||
public GetActivityAsyncTask(final OnTaskCompleteListener onTaskCompleteListener) {
|
||||
this.onTaskCompleteListener = onTaskCompleteListener;
|
||||
}
|
||||
|
||||
/*
|
||||
This needs to be redone to fetch i inbox instead
|
||||
Within inbox, data is (body JSON => counts)
|
||||
Then we have these counts:
|
||||
new_posts, activity_feed_dot_badge, relationships, campaign_notification
|
||||
usertags, likes, comment_likes, shopping_notification, comments
|
||||
photos_of_you (not sure about difference to usertags), requests
|
||||
*/
|
||||
|
||||
protected NotificationCounts doInBackground(final String... cookiesArray) {
|
||||
if (cookiesArray == null) return null;
|
||||
final String cookie = cookiesArray[0];
|
||||
@ -70,11 +79,11 @@ public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsy
|
||||
}
|
||||
|
||||
public static class NotificationCounts {
|
||||
private int relationshipsCount;
|
||||
private int userTagsCount;
|
||||
private int commentsCount;
|
||||
private int commentLikesCount;
|
||||
private int likesCount;
|
||||
private final int relationshipsCount;
|
||||
private final int userTagsCount;
|
||||
private final int commentsCount;
|
||||
private final int commentLikesCount;
|
||||
private final int likesCount;
|
||||
|
||||
public NotificationCounts(final int relationshipsCount,
|
||||
final int userTagsCount,
|
||||
|
@ -89,6 +89,7 @@ public final class HashtagFetcher extends AsyncTask<Void, Void, HashtagModel> {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_HASHTAG_FETCHER, "doInBackground");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
if (fetchListener != null) fetchListener.onFailure(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.HashtagModel;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.webservices.GraphQLService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awais.instagrabber.webservices.TagsService;
|
||||
import awais.instagrabber.webservices.TagsService.TagPostsFetchResponse;
|
||||
|
||||
public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
||||
private final TagsService tagsService;
|
||||
private final GraphQLService graphQLService;
|
||||
private final HashtagModel hashtagModel;
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable;
|
||||
@ -20,19 +22,20 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
||||
public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) {
|
||||
this.hashtagModel = hashtagModel;
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
tagsService = TagsService.getInstance();
|
||||
tagsService = isLoggedIn ? TagsService.getInstance() : null;
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
final ServiceCallback cb = new ServiceCallback<TagPostsFetchResponse>() {
|
||||
final ServiceCallback cb = new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final TagPostsFetchResponse result) {
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
nextMaxId = result.getNextMaxId();
|
||||
moreAvailable = result.isMoreAvailable();
|
||||
nextMaxId = result.getNextCursor();
|
||||
moreAvailable = result.hasNextPage();
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getItems());
|
||||
fetchListener.onResult(result.getFeedModels());
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
};
|
||||
if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
|
||||
else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
|
||||
else graphQLService.fetchHashtagPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.LocationModel;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.webservices.GraphQLService;
|
||||
import awais.instagrabber.webservices.LocationService;
|
||||
import awais.instagrabber.webservices.LocationService.LocationPostsFetchResponse;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
||||
private final LocationService locationService;
|
||||
private final GraphQLService graphQLService;
|
||||
private final LocationModel locationModel;
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable;
|
||||
@ -20,19 +22,20 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
||||
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) {
|
||||
this.locationModel = locationModel;
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
locationService = LocationService.getInstance();
|
||||
locationService = isLoggedIn ? LocationService.getInstance() : null;
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
final ServiceCallback cb = new ServiceCallback<LocationPostsFetchResponse>() {
|
||||
final ServiceCallback cb = new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final LocationPostsFetchResponse result) {
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
nextMaxId = result.getNextMaxId();
|
||||
moreAvailable = result.isMoreAvailable();
|
||||
nextMaxId = result.getNextCursor();
|
||||
moreAvailable = result.hasNextPage();
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getItems());
|
||||
fetchListener.onResult(result.getFeedModels());
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
};
|
||||
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb);
|
||||
else locationService.fetchGraphQLPosts(locationModel.getId(), nextMaxId, cb);
|
||||
else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,21 +3,14 @@ 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.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.LocaleUtils;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
import awais.instagrabber.webservices.NewsService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
@ -26,89 +19,48 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, List<Notif
|
||||
private static final String TAG = "NotificationsFetcher";
|
||||
|
||||
private final FetchListener<List<NotificationModel>> fetchListener;
|
||||
private final NewsService newsService;
|
||||
private final boolean markAsSeen;
|
||||
private boolean fetchedWeb = false;
|
||||
|
||||
public NotificationsFetcher(final FetchListener<List<NotificationModel>> fetchListener) {
|
||||
public NotificationsFetcher(final boolean markAsSeen,
|
||||
final FetchListener<List<NotificationModel>> fetchListener) {
|
||||
this.markAsSeen = markAsSeen;
|
||||
this.fetchListener = fetchListener;
|
||||
newsService = NewsService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<NotificationModel> doInBackground(final Void... voids) {
|
||||
List<NotificationModel> result = new ArrayList<>();
|
||||
final String url = "https://www.instagram.com/accounts/activity/?__a=1";
|
||||
try {
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
|
||||
conn.connect();
|
||||
List<NotificationModel> notificationModels = new ArrayList<>();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
final JSONObject page = new JSONObject(NetworkUtils.readFromConnection(conn))
|
||||
.getJSONObject("graphql")
|
||||
.getJSONObject("user");
|
||||
final JSONObject ewaf = page.getJSONObject("activity_feed")
|
||||
.optJSONObject("edge_web_activity_feed");
|
||||
final JSONObject efr = page.optJSONObject("edge_follow_requests");
|
||||
JSONObject data;
|
||||
JSONArray media;
|
||||
if (ewaf != null
|
||||
&& (media = ewaf.optJSONArray("edges")) != null
|
||||
&& media.length() > 0
|
||||
&& media.optJSONObject(0).optJSONObject("node") != null) {
|
||||
for (int i = 0; i < media.length(); ++i) {
|
||||
data = media.optJSONObject(i).optJSONObject("node");
|
||||
if (data == null) continue;
|
||||
final String type = data.getString("__typename");
|
||||
final NotificationType notificationType = NotificationType.valueOfType(type);
|
||||
if (notificationType == null) continue;
|
||||
final JSONObject user = data.getJSONObject("user");
|
||||
result.add(new NotificationModel(
|
||||
data.getString(Constants.EXTRAS_ID),
|
||||
data.optString("text"), // comments or mentions
|
||||
data.getLong("timestamp"),
|
||||
user.getString("id"),
|
||||
user.getString("username"),
|
||||
user.getString("profile_pic_url"),
|
||||
!data.isNull("media") ? data.getJSONObject("media").getString("shortcode") : null,
|
||||
!data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, notificationType));
|
||||
}
|
||||
newsService.fetchAppInbox(markAsSeen, new ServiceCallback<List<NotificationModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<NotificationModel> result) {
|
||||
if (result == null) return;
|
||||
notificationModels.addAll(result);
|
||||
if (fetchedWeb) {
|
||||
fetchListener.onResult(notificationModels);
|
||||
}
|
||||
|
||||
if (efr != null
|
||||
&& (media = efr.optJSONArray("edges")) != null
|
||||
&& media.length() > 0
|
||||
&& media.optJSONObject(0).optJSONObject("node") != null) {
|
||||
for (int i = 0; i < media.length(); ++i) {
|
||||
data = media.optJSONObject(i).optJSONObject("node");
|
||||
if (data == null) continue;
|
||||
result.add(new NotificationModel(
|
||||
data.getString(Constants.EXTRAS_ID),
|
||||
data.optString("full_name"),
|
||||
0L,
|
||||
data.getString(Constants.EXTRAS_ID),
|
||||
data.getString("username"),
|
||||
data.getString("profile_pic_url"),
|
||||
null,
|
||||
null, NotificationType.REQUEST));
|
||||
}
|
||||
else {
|
||||
fetchedWeb = true;
|
||||
newsService.fetchWebInbox(markAsSeen, this);
|
||||
}
|
||||
}
|
||||
conn.disconnect();
|
||||
} catch (final Exception e) {
|
||||
if (logCollector != null)
|
||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_NOTIFICATION_FETCHER, "doInBackground");
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
return result;
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
return notificationModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (fetchListener != null) fetchListener.doBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final List<NotificationModel> result) {
|
||||
if (fetchListener != null) fetchListener.onResult(result);
|
||||
}
|
||||
}
|
@ -65,6 +65,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, FeedModel> {
|
||||
owner.optInt("edge_followed_by"),
|
||||
-1,
|
||||
owner.optBoolean("followed_by_viewer"),
|
||||
false,
|
||||
owner.optBoolean("restricted_by_viewer"),
|
||||
owner.optBoolean("blocked_by_viewer"),
|
||||
owner.optBoolean("requested_by_viewer")
|
||||
|
@ -72,6 +72,7 @@ public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
|
||||
user.getJSONObject("edge_followed_by").getLong("count"),
|
||||
user.getJSONObject("edge_follow").getLong("count"),
|
||||
user.optBoolean("followed_by_viewer"),
|
||||
user.optBoolean("follows_viewer"),
|
||||
user.optBoolean("restricted_by_viewer"),
|
||||
user.optBoolean("blocked_by_viewer"),
|
||||
user.optBoolean("requested_by_viewer"));
|
||||
|
@ -40,51 +40,34 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
|
||||
protected String doInBackground(final Void... voids) {
|
||||
String out = null;
|
||||
if (isHashtag) out = picUrl;
|
||||
else try {
|
||||
final HttpURLConnection conn =
|
||||
(HttpURLConnection) new URL("https://i.instagram.com/api/v1/users/"+userId+"/info/").openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
|
||||
else if (Utils.settingsHelper.getBoolean(Constants.INSTADP)) try {
|
||||
final HttpURLConnection backup =
|
||||
(HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection();
|
||||
backup.setUseCaches(false);
|
||||
backup.setRequestMethod("GET");
|
||||
backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
|
||||
|
||||
final String result = conn.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(conn) : null;
|
||||
conn.disconnect();
|
||||
final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(backup) : null;
|
||||
backup.disconnect();
|
||||
|
||||
if (!TextUtils.isEmpty(result)) {
|
||||
JSONObject data = new JSONObject(result).getJSONObject("user");
|
||||
if (data.has("hd_profile_pic_url_info"))
|
||||
out = data.getJSONObject("hd_profile_pic_url_info").optString("url");
|
||||
}
|
||||
if (!TextUtils.isEmpty(instadp)) {
|
||||
final Document doc = Jsoup.parse(instadp);
|
||||
boolean fallback = false;
|
||||
|
||||
if (TextUtils.isEmpty(out) && Utils.settingsHelper.getBoolean(Constants.INSTADP)) {
|
||||
final HttpURLConnection backup =
|
||||
(HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection();
|
||||
backup.setUseCaches(false);
|
||||
backup.setRequestMethod("GET");
|
||||
backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
|
||||
final int imgIndex = instadp.indexOf("preloadImg('"), lastIndex;
|
||||
|
||||
final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(backup) : null;
|
||||
backup.disconnect();
|
||||
|
||||
if (!TextUtils.isEmpty(instadp)) {
|
||||
final Document doc = Jsoup.parse(instadp);
|
||||
boolean fallback = false;
|
||||
|
||||
final int imgIndex = instadp.indexOf("preloadImg('"), lastIndex;
|
||||
|
||||
Element element = doc.selectFirst(".instadp");
|
||||
if (element != null && (element = element.selectFirst(".picture")) != null)
|
||||
out = element.attr("src");
|
||||
else if ((element = doc.selectFirst(".download-btn")) != null)
|
||||
out = element.attr("href");
|
||||
else if (imgIndex != -1 && (lastIndex = instadp.indexOf("')", imgIndex)) != -1)
|
||||
out = instadp.substring(imgIndex + 12, lastIndex);
|
||||
else {
|
||||
final Elements imgs = doc.getElementsByTag("img");
|
||||
for (final Element img : imgs) {
|
||||
final String imgStr = img.toString();
|
||||
if (imgStr.contains("cdninstagram.com")) out = img.attr("src");
|
||||
}
|
||||
Element element = doc.selectFirst(".instadp");
|
||||
if (element != null && (element = element.selectFirst(".picture")) != null)
|
||||
out = element.attr("src");
|
||||
else if ((element = doc.selectFirst(".download-btn")) != null)
|
||||
out = element.attr("href");
|
||||
else if (imgIndex != -1 && (lastIndex = instadp.indexOf("')", imgIndex)) != -1)
|
||||
out = instadp.substring(imgIndex + 12, lastIndex);
|
||||
else {
|
||||
final Elements imgs = doc.getElementsByTag("img");
|
||||
for (final Element img : imgs) {
|
||||
final String imgStr = img.toString();
|
||||
if (imgStr.contains("cdninstagram.com")) out = img.attr("src");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,29 +7,34 @@ import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.webservices.GraphQLService;
|
||||
import awais.instagrabber.webservices.ProfileService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||
private static final String TAG = "ProfilePostFetchService";
|
||||
private final ProfileService profileService;
|
||||
private final GraphQLService graphQLService;
|
||||
private final ProfileModel profileModel;
|
||||
private String nextCursor;
|
||||
private boolean hasNextPage;
|
||||
private final boolean isLoggedIn;
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable;
|
||||
|
||||
public ProfilePostFetchService(final ProfileModel profileModel) {
|
||||
public ProfilePostFetchService(final ProfileModel profileModel, final boolean isLoggedIn) {
|
||||
this.profileModel = profileModel;
|
||||
profileService = ProfileService.getInstance();
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
|
||||
profileService = isLoggedIn ? ProfileService.getInstance() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
final ServiceCallback cb = new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
nextCursor = result.getNextCursor();
|
||||
hasNextPage = result.hasNextPage();
|
||||
nextMaxId = result.getNextCursor();
|
||||
moreAvailable = result.hasNextPage();
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getFeedModels());
|
||||
}
|
||||
@ -42,16 +47,18 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||
fetchListener.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (isLoggedIn) profileService.fetchPosts(profileModel.getId(), nextMaxId, cb);
|
||||
else graphQLService.fetchProfilePosts(profileModel.getId(), 30, nextMaxId, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
nextCursor = null;
|
||||
nextMaxId = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextPage() {
|
||||
return hasNextPage;
|
||||
return moreAvailable;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -6,34 +6,39 @@ import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.webservices.GraphQLService;
|
||||
import awais.instagrabber.webservices.ProfileService;
|
||||
import awais.instagrabber.webservices.ProfileService.SavedPostsFetchResponse;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public class SavedPostFetchService implements PostFetcher.PostFetchService {
|
||||
private final ProfileService profileService;
|
||||
private final GraphQLService graphQLService;
|
||||
private final String profileId;
|
||||
private final PostItemType type;
|
||||
private final boolean isLoggedIn;
|
||||
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable;
|
||||
|
||||
public SavedPostFetchService(final String profileId, final PostItemType type) {
|
||||
public SavedPostFetchService(final String profileId, final PostItemType type, final boolean isLoggedIn) {
|
||||
this.profileId = profileId;
|
||||
this.type = type;
|
||||
profileService = ProfileService.getInstance();
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
|
||||
profileService = isLoggedIn ? ProfileService.getInstance() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
final ServiceCallback<SavedPostsFetchResponse> callback = new ServiceCallback<SavedPostsFetchResponse>() {
|
||||
final ServiceCallback<PostsFetchResponse> callback = new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final SavedPostsFetchResponse result) {
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
nextMaxId = result.getNextMaxId();
|
||||
moreAvailable = result.isMoreAvailable();
|
||||
nextMaxId = result.getNextCursor();
|
||||
moreAvailable = result.hasNextPage();
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getItems());
|
||||
fetchListener.onResult(result.getFeedModels());
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +55,8 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService {
|
||||
profileService.fetchLiked(nextMaxId, callback);
|
||||
break;
|
||||
case TAGGED:
|
||||
profileService.fetchTagged(profileId, nextMaxId, callback);
|
||||
if (isLoggedIn) profileService.fetchTagged(profileId, nextMaxId, callback);
|
||||
else graphQLService.fetchTaggedPosts(profileId, 30, nextMaxId, callback);
|
||||
break;
|
||||
case SAVED:
|
||||
default:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -122,4 +122,131 @@
|
||||
// public interface OnBroadcastCompleteListener {
|
||||
// void onTaskComplete(DirectThreadBroadcastResponse response);
|
||||
// }
|
||||
//
|
||||
// public enum ItemType {
|
||||
// TEXT("text"),
|
||||
// REACTION("reaction"),
|
||||
// REELSHARE("reel_share"),
|
||||
// IMAGE("configure_photo");
|
||||
//
|
||||
// private final String value;
|
||||
//
|
||||
// ItemType(final String value) {
|
||||
// this.value = value;
|
||||
// }
|
||||
//
|
||||
// public String getValue() {
|
||||
// return value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static abstract class BroadcastOptions {
|
||||
// private final ItemType itemType;
|
||||
//
|
||||
// public BroadcastOptions(final ItemType itemType) {
|
||||
// this.itemType = itemType;
|
||||
// }
|
||||
//
|
||||
// public ItemType getItemType() {
|
||||
// return itemType;
|
||||
// }
|
||||
//
|
||||
// abstract Map<String, String> getFormMap();
|
||||
// }
|
||||
//
|
||||
// public static class TextBroadcastOptions extends BroadcastOptions {
|
||||
// private final String text;
|
||||
//
|
||||
// public TextBroadcastOptions(String text) throws UnsupportedEncodingException {
|
||||
// super(ItemType.TEXT);
|
||||
// this.text = URLEncoder.encode(text, "UTF-8")
|
||||
// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'").replaceAll("%22", "\\\"")
|
||||
// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// Map<String, String> getFormMap() {
|
||||
// return Collections.singletonMap("text", text);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static class ReactionBroadcastOptions extends BroadcastOptions {
|
||||
// private final String itemId;
|
||||
// private final boolean delete;
|
||||
//
|
||||
// public ReactionBroadcastOptions(String itemId, boolean delete) {
|
||||
// super(ItemType.REACTION);
|
||||
// this.itemId = itemId;
|
||||
// this.delete = delete;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// Map<String, String> getFormMap() {
|
||||
// final Map<String, String> form = new HashMap<>();
|
||||
// form.put("item_id", itemId);
|
||||
// form.put("reaction_status", delete ? "deleted" : "created");
|
||||
// form.put("reaction_type", "like");
|
||||
// return form;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static class StoryReplyBroadcastOptions extends BroadcastOptions {
|
||||
// private final String text, mediaId, reelId;
|
||||
//
|
||||
// public StoryReplyBroadcastOptions(String text, String mediaId, String reelId) throws UnsupportedEncodingException {
|
||||
// super(ItemType.REELSHARE);
|
||||
// this.text = URLEncoder.encode(text, "UTF-8")
|
||||
// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
|
||||
// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
|
||||
// this.mediaId = mediaId;
|
||||
// this.reelId = reelId; // or user id, usually same
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// Map<String, String> getFormMap() {
|
||||
// final Map<String, String> form = new HashMap<>();
|
||||
// form.put("text", text);
|
||||
// form.put("media_id", mediaId);
|
||||
// form.put("reel_id", reelId);
|
||||
// form.put("entry", "reel");
|
||||
// return form;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static class ImageBroadcastOptions extends BroadcastOptions {
|
||||
// final boolean allowFullAspectRatio;
|
||||
// final String uploadId;
|
||||
//
|
||||
// public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) {
|
||||
// super(ItemType.IMAGE);
|
||||
// this.allowFullAspectRatio = allowFullAspectRatio;
|
||||
// this.uploadId = uploadId;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// Map<String, String> getFormMap() {
|
||||
// final Map<String, String> form = new HashMap<>();
|
||||
// form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio));
|
||||
// form.put("upload_id", uploadId);
|
||||
// return form;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static class DirectThreadBroadcastResponse {
|
||||
// private final int responseCode;
|
||||
// private final JSONObject response;
|
||||
//
|
||||
// public DirectThreadBroadcastResponse(int responseCode, JSONObject response) {
|
||||
// this.responseCode = responseCode;
|
||||
// this.response = response;
|
||||
// }
|
||||
//
|
||||
// public int getResponseCode() {
|
||||
// return responseCode;
|
||||
// }
|
||||
//
|
||||
// public JSONObject getResponse() {
|
||||
// return response;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
@ -183,11 +183,13 @@ public class PostsRecyclerView extends RecyclerView {
|
||||
|
||||
private void initSelf() {
|
||||
feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
|
||||
feedViewModel.getList().observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
|
||||
if (!shouldScrollToTop) return;
|
||||
smoothScrollToPosition(0);
|
||||
shouldScrollToTop = false;
|
||||
}));
|
||||
feedViewModel.getList().observe(lifeCycleOwner, list -> {
|
||||
if (list.size() > 0) feedAdapter.submitList(list, () -> {
|
||||
if (!shouldScrollToTop) return;
|
||||
smoothScrollToPosition(0);
|
||||
shouldScrollToTop = false;
|
||||
});
|
||||
});
|
||||
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
||||
if (layoutPreferences.getHasGap()) {
|
||||
addItemDecoration(gridSpacingItemDecoration);
|
||||
|
@ -161,7 +161,7 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||
final FavoriteType type = favoriteTypeQueryPair.first;
|
||||
final String query = favoriteTypeQueryPair.second;
|
||||
oldModels.add(new Favorite(
|
||||
-1,
|
||||
0,
|
||||
query,
|
||||
type,
|
||||
queryDisplayExists ? cursor.getString(cursor.getColumnIndex("query_display"))
|
||||
|
@ -21,7 +21,7 @@ public interface FavoriteDao {
|
||||
@Query("SELECT * FROM favorites WHERE query_text = :query and type = :type")
|
||||
Favorite findFavoriteByQueryAndType(String query, FavoriteType type);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
@Insert
|
||||
List<Long> insertFavorites(Favorite... favorites);
|
||||
|
||||
@Update
|
||||
|
@ -9,6 +9,7 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -19,6 +20,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.controller.BaseControllerListener;
|
||||
@ -30,9 +32,19 @@ import java.io.File;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.asyncs.ProfilePictureFetcher;
|
||||
import awais.instagrabber.databinding.DialogProfilepicBinding;
|
||||
import awais.instagrabber.db.entities.Account;
|
||||
import awais.instagrabber.db.repositories.RepositoryCallback;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.repositories.responses.UserInfo;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.webservices.ProfileService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class ProfilePicDialogFragment extends DialogFragment {
|
||||
private static final String TAG = "ProfilePicDlgFragment";
|
||||
@ -41,9 +53,15 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
private final String name;
|
||||
private final String fallbackUrl;
|
||||
|
||||
private boolean isLoggedIn;
|
||||
private DialogProfilepicBinding binding;
|
||||
private String url;
|
||||
|
||||
private final FetchListener<String> fetchListener = profileUrl -> {
|
||||
url = profileUrl;
|
||||
setupPhoto();
|
||||
};
|
||||
|
||||
public ProfilePicDialogFragment(final String id, final String name, final String fallbackUrl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@ -55,6 +73,8 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
binding = DialogProfilepicBinding.inflate(inflater, container, false);
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@ -83,7 +103,7 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
init();
|
||||
fetchPhoto();
|
||||
fetchAvatar();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
@ -106,37 +126,55 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchPhoto() {
|
||||
final FetchListener<String> fetchListener = profileUrl -> {
|
||||
url = profileUrl;
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
url = fallbackUrl;
|
||||
}
|
||||
final DraweeController controller = Fresco
|
||||
.newDraweeControllerBuilder()
|
||||
.setUri(url)
|
||||
.setOldController(binding.imageViewer.getController())
|
||||
.setControllerListener(new BaseControllerListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onFailure(final String id, final Throwable throwable) {
|
||||
super.onFailure(id, throwable);
|
||||
binding.download.setVisibility(View.GONE);
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
private void fetchAvatar() {
|
||||
if (isLoggedIn) {
|
||||
final ProfileService profileService = ProfileService.getInstance();
|
||||
profileService.getUserInfo(id, new ServiceCallback<UserInfo>() {
|
||||
@Override
|
||||
public void onSuccess(final UserInfo result) {
|
||||
if (result != null) {
|
||||
fetchListener.onResult(result.getHDProfilePicUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalImageSet(final String id,
|
||||
final ImageInfo imageInfo,
|
||||
final Animatable animatable) {
|
||||
super.onFinalImageSet(id, imageInfo, animatable);
|
||||
binding.download.setVisibility(View.VISIBLE);
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
binding.imageViewer.setController(controller);
|
||||
};
|
||||
new ProfilePictureFetcher(name, id, fetchListener, url, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
final Context context = getContext();
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
getDialog().dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
else new ProfilePictureFetcher(name, id, fetchListener, fallbackUrl, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void setupPhoto() {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
url = fallbackUrl;
|
||||
}
|
||||
final DraweeController controller = Fresco
|
||||
.newDraweeControllerBuilder()
|
||||
.setUri(url)
|
||||
.setOldController(binding.imageViewer.getController())
|
||||
.setControllerListener(new BaseControllerListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onFailure(final String id, final Throwable throwable) {
|
||||
super.onFailure(id, throwable);
|
||||
binding.download.setVisibility(View.GONE);
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalImageSet(final String id,
|
||||
final ImageInfo imageInfo,
|
||||
final Animatable animatable) {
|
||||
super.onFinalImageSet(id, imageInfo, animatable);
|
||||
binding.download.setVisibility(View.VISIBLE);
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
binding.imageViewer.setController(controller);
|
||||
}
|
||||
|
||||
private void downloadProfilePicture() {
|
||||
|
@ -25,6 +25,7 @@ import androidx.appcompat.widget.LinearLayoutCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@ -32,11 +33,18 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.CommentsAdapter;
|
||||
import awais.instagrabber.asyncs.CommentsFetcher;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.CommentModel;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
@ -56,17 +64,48 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
|
||||
private CommentsAdapter commentsAdapter;
|
||||
private FragmentCommentsBinding binding;
|
||||
private String shortCode;
|
||||
private String userId;
|
||||
private LinearLayoutManager layoutManager;
|
||||
private RecyclerLazyLoader lazyLoader;
|
||||
private String shortCode, userId, endCursor = null;
|
||||
private Resources resources;
|
||||
private InputMethodManager imm;
|
||||
private AppCompatActivity fragmentActivity;
|
||||
private LinearLayoutCompat root;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean shouldRefresh = true, hasNextPage = false;
|
||||
private MediaService mediaService;
|
||||
private String postId;
|
||||
private AsyncTask<Void, Void, List<CommentModel>> currentlyRunning;
|
||||
private CommentsViewModel commentsViewModel;
|
||||
|
||||
private final FetchListener<List<CommentModel>> fetchListener = new FetchListener<List<CommentModel>>() {
|
||||
@Override
|
||||
public void doBefore() {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(final List<CommentModel> commentModels) {
|
||||
if (commentModels != null && commentModels.size() > 0) {
|
||||
endCursor = commentModels.get(0).getEndCursor();
|
||||
hasNextPage = commentModels.get(0).hasNextPage();
|
||||
List<CommentModel> list = commentsViewModel.getList().getValue();
|
||||
list = list != null ? new LinkedList<>(list) : new LinkedList<>();
|
||||
// final int oldSize = list != null ? list.size() : 0;
|
||||
list.addAll(commentModels);
|
||||
commentsViewModel.getList().postValue(list);
|
||||
}
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
stopCurrentExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
stopCurrentExecutor();
|
||||
}
|
||||
};
|
||||
|
||||
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
|
||||
@Override
|
||||
public void onClick(final CommentModel comment) {
|
||||
@ -181,11 +220,11 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
new CommentsFetcher(shortCode, commentModels -> {
|
||||
commentsViewModel.getList().postValue(commentModels);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
endCursor = null;
|
||||
lazyLoader.resetState();
|
||||
commentsViewModel.getList().postValue(Collections.emptyList());
|
||||
stopCurrentExecutor();
|
||||
currentlyRunning = new CommentsFetcher(shortCode, "", fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
@ -198,7 +237,8 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
|
||||
binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
binding.rvComments.setLayoutManager(layoutManager);
|
||||
commentsAdapter = new CommentsAdapter(commentCallback);
|
||||
binding.rvComments.setAdapter(commentsAdapter);
|
||||
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
|
||||
@ -226,6 +266,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
});
|
||||
binding.commentField.setEndIconOnClickListener(newCommentListener);
|
||||
}
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (hasNextPage && !TextUtils.isEmpty(endCursor))
|
||||
currentlyRunning = new CommentsFetcher(shortCode, endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
endCursor = null;
|
||||
});
|
||||
binding.rvComments.addOnScrollListener(lazyLoader);
|
||||
stopCurrentExecutor();
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@ -249,30 +296,29 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
|
||||
commentDialogList = new String[]{
|
||||
resources.getString(R.string.open_profile),
|
||||
resources.getString(R.string.view_pfp),
|
||||
// resources.getString(R.string.comment_viewer_copy_user),
|
||||
resources.getString(R.string.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers),
|
||||
resources.getString(R.string.comment_viewer_reply_comment),
|
||||
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
||||
: resources.getString(R.string.comment_viewer_like_comment),
|
||||
resources.getString(R.string.comment_viewer_translate_comment),
|
||||
resources.getString(R.string.comment_viewer_delete_comment)
|
||||
};
|
||||
} else if (!TextUtils.isEmpty(cookie)) {
|
||||
commentDialogList = new String[]{
|
||||
resources.getString(R.string.open_profile),
|
||||
resources.getString(R.string.view_pfp),
|
||||
// resources.getString(R.string.comment_viewer_copy_user),
|
||||
resources.getString(R.string.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers),
|
||||
resources.getString(R.string.comment_viewer_reply_comment),
|
||||
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
||||
: resources.getString(R.string.comment_viewer_like_comment),
|
||||
resources.getString(R.string.comment_viewer_translate_comment)
|
||||
};
|
||||
} else {
|
||||
commentDialogList = new String[]{
|
||||
resources.getString(R.string.open_profile),
|
||||
resources.getString(R.string.view_pfp),
|
||||
// resources.getString(R.string.comment_viewer_copy_user),
|
||||
resources.getString(R.string.comment_viewer_copy_comment)
|
||||
resources.getString(R.string.comment_viewer_copy_comment),
|
||||
resources.getString(R.string.comment_viewer_see_likers)
|
||||
};
|
||||
}
|
||||
final Context context = getContext();
|
||||
@ -284,25 +330,20 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
case 0: // open profile
|
||||
openProfile("@" + profileModel.getUsername());
|
||||
break;
|
||||
case 1: // view profile pic
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
|
||||
profileModel.getUsername(),
|
||||
profileModel.getHdProfilePic());
|
||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.add(fragment, "profilePicDialog")
|
||||
.commit();
|
||||
break;
|
||||
// case 2: // copy username
|
||||
// Utils.copyText(context, profileModel.getUsername());
|
||||
// break;
|
||||
case 2: // copy comment
|
||||
case 1: // copy comment
|
||||
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
|
||||
break;
|
||||
case 2: // see comment likers, this is surprisingly available to anons
|
||||
final NavController navController = getNavController();
|
||||
if (navController != null) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("postId", commentModel.getId());
|
||||
bundle.putBoolean("isComment", true);
|
||||
navController.navigate(R.id.action_global_likesViewerFragment, bundle);
|
||||
}
|
||||
else Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case 3: // reply to comment
|
||||
// final View focus = binding.rvComments.findViewWithTag(commentModel);
|
||||
// focus.setBackgroundColor(0x80888888);
|
||||
commentsAdapter.setSelected(commentModel);
|
||||
String mention = "@" + profileModel.getUsername() + " ";
|
||||
binding.commentText.setText(mention);
|
||||
@ -326,7 +367,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
commentsAdapter.setLiked(commentModel, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -344,7 +385,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
commentsAdapter.setLiked(commentModel, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -354,7 +395,29 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 5: // delete comment
|
||||
case 5: // translate comment
|
||||
mediaService.translate(commentModel.getId(), "2", new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
if (TextUtils.isEmpty(result)) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(username)
|
||||
.setMessage(result)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error translating comment", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 6: // delete comment
|
||||
final String userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
if (userId == null) return;
|
||||
mediaService.deleteComment(
|
||||
@ -389,4 +452,25 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
|
||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
}
|
||||
|
||||
private void stopCurrentExecutor() {
|
||||
if (currentlyRunning != null) {
|
||||
try {
|
||||
currentlyRunning.cancel(true);
|
||||
} catch (final Exception e) {
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NavController getNavController() {
|
||||
NavController navController = null;
|
||||
try {
|
||||
navController = NavHostFragment.findNavController(this);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "navigateToProfile", e);
|
||||
}
|
||||
return navController;
|
||||
}
|
||||
}
|
@ -81,7 +81,6 @@ public class FavoritesFragment extends Fragment {
|
||||
@Override
|
||||
public void onSuccess(final List<Favorite> favorites) {
|
||||
favoritesViewModel.getList().postValue(favorites);
|
||||
fetchMissingInfo(favorites);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,109 +155,4 @@ public class FavoritesFragment extends Fragment {
|
||||
binding.favoriteList.setAdapter(adapter);
|
||||
|
||||
}
|
||||
|
||||
private void fetchMissingInfo(final List<Favorite> allFavorites) {
|
||||
final Runnable runnable = () -> {
|
||||
final List<Favorite> updatedList = new ArrayList<>(allFavorites);
|
||||
// cyclic barrier is to make the async calls synchronous
|
||||
final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
|
||||
// Log.d(TAG, "fetchMissingInfo: barrier action");
|
||||
favoritesViewModel.getList().postValue(new ArrayList<>(updatedList));
|
||||
});
|
||||
try {
|
||||
for (final Favorite model : allFavorites) {
|
||||
cyclicBarrier.reset();
|
||||
// if the model has missing pic or display name (for user and location), fetch those details
|
||||
switch (model.getType()) {
|
||||
case LOCATION:
|
||||
if (TextUtils.isEmpty(model.getDisplayName())
|
||||
|| TextUtils.isEmpty(model.getPicUrl())) {
|
||||
new LocationFetcher(model.getQuery(), result -> {
|
||||
if (result == null) return;
|
||||
final int i = updatedList.indexOf(model);
|
||||
updatedList.remove(i);
|
||||
final Favorite updated = new Favorite(
|
||||
model.getId(),
|
||||
model.getQuery(),
|
||||
model.getType(),
|
||||
result.getName(),
|
||||
result.getSdProfilePic(),
|
||||
model.getDateAdded()
|
||||
);
|
||||
favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
updatedList.add(i, updated);
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).execute();
|
||||
cyclicBarrier.await();
|
||||
}
|
||||
break;
|
||||
case USER:
|
||||
if (TextUtils.isEmpty(model.getDisplayName())
|
||||
|| TextUtils.isEmpty(model.getPicUrl())) {
|
||||
new ProfileFetcher(model.getQuery(), result -> {
|
||||
if (result == null) return;
|
||||
final int i = updatedList.indexOf(model);
|
||||
updatedList.remove(i);
|
||||
final Favorite updated = new Favorite(
|
||||
model.getId(),
|
||||
model.getQuery(),
|
||||
model.getType(),
|
||||
result.getName(),
|
||||
result.getSdProfilePic(),
|
||||
model.getDateAdded()
|
||||
);
|
||||
favoriteRepository.insertOrUpdateFavorite(updated, new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
try {
|
||||
cyclicBarrier.await();
|
||||
} catch (BrokenBarrierException | InterruptedException e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
updatedList.add(i, updated);
|
||||
}).execute();
|
||||
cyclicBarrier.await();
|
||||
}
|
||||
break;
|
||||
case HASHTAG:
|
||||
default:
|
||||
// hashtags don't require displayName or pic
|
||||
// updatedList.add(model);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "fetchMissingInfo: ", e);
|
||||
}
|
||||
favoritesViewModel.getList().postValue(updatedList);
|
||||
};
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
}
|
||||
|
@ -20,29 +20,22 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.FollowAdapter;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentFollowersViewerBinding;
|
||||
import awais.instagrabber.models.FollowModel;
|
||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||
import awais.instagrabber.repositories.responses.FriendshipRepoListFetchResponse;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.webservices.FriendshipService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awaisomereport.LogCollector;
|
||||
import thoughtbot.expandableadapter.ExpandableGroup;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class FollowViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "FollowViewerFragment";
|
||||
|
||||
@ -51,9 +44,11 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
private final ArrayList<FollowModel> followersModels = new ArrayList<>();
|
||||
private final ArrayList<FollowModel> allFollowing = new ArrayList<>();
|
||||
|
||||
private boolean isFollowersList, isCompare = false, loading = false;
|
||||
private String profileId, username, namePost, type;
|
||||
private boolean moreAvailable = true, isFollowersList, isCompare = false, loading = false, shouldRefresh = true;
|
||||
private String profileId, username, namePost, type, endCursor;
|
||||
private Resources resources;
|
||||
private LinearLayoutManager layoutManager;
|
||||
private RecyclerLazyLoader lazyLoader;
|
||||
private FollowModel model;
|
||||
private FollowAdapter adapter;
|
||||
private View.OnClickListener clickListener;
|
||||
@ -61,9 +56,61 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
|
||||
private SwipeRefreshLayout root;
|
||||
private FriendshipService friendshipService;
|
||||
private boolean shouldRefresh = true;
|
||||
private AppCompatActivity fragmentActivity;
|
||||
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> followingFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||
if (result != null) {
|
||||
followingModels.addAll(result.getItems());
|
||||
if (!isFollowersList) followModels.addAll(result.getItems());
|
||||
if (result.isMoreAvailable()) {
|
||||
endCursor = result.getNextMaxId();
|
||||
friendshipService.getList(false, profileId, endCursor, this);
|
||||
} else if (followersModels.size() == 0) {
|
||||
if (!isFollowersList) moreAvailable = false;
|
||||
friendshipService.getList(true, profileId, null, followingFetchCb);
|
||||
} else {
|
||||
if (!isFollowersList) moreAvailable = false;
|
||||
showCompare();
|
||||
}
|
||||
} else binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Error fetching list (double, following)", t);
|
||||
}
|
||||
};
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> followersFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||
if (result != null) {
|
||||
followersModels.addAll(result.getItems());
|
||||
if (isFollowersList) followModels.addAll(result.getItems());
|
||||
if (result.isMoreAvailable()) {
|
||||
endCursor = result.getNextMaxId();
|
||||
friendshipService.getList(true, profileId, endCursor, this);
|
||||
} else if (followingModels.size() == 0) {
|
||||
if (isFollowersList) moreAvailable = false;
|
||||
friendshipService.getList(false, profileId, null, followingFetchCb);
|
||||
} else {
|
||||
if (isFollowersList) moreAvailable = false;
|
||||
showCompare();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Error fetching list (double, follower)", t);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -141,13 +188,13 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
public void onRefresh() {
|
||||
if (isCompare) listCompare();
|
||||
else listFollows();
|
||||
endCursor = null;
|
||||
lazyLoader.resetState();
|
||||
}
|
||||
|
||||
private void listFollows() {
|
||||
loading = true;
|
||||
type = resources.getString(isFollowersList ? R.string.followers_type_followers : R.string.followers_type_following);
|
||||
setSubtitle(type);
|
||||
followModels.clear();
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> cb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||
@ -156,82 +203,70 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
return;
|
||||
}
|
||||
else {
|
||||
int oldSize = followModels.size() == 0 ? 0 : followModels.size() - 1;
|
||||
followModels.addAll(result.getItems());
|
||||
if (result.isMoreAvailable()) {
|
||||
friendshipService.getList(isFollowersList, profileId, result.getNextMaxId(), this);
|
||||
}
|
||||
else {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (isFollowersList) followersModels.addAll(followModels);
|
||||
else followingModels.addAll(followModels);
|
||||
refreshAdapter(followModels, null, null, null);
|
||||
moreAvailable = true;
|
||||
endCursor = result.getNextMaxId();
|
||||
}
|
||||
else moreAvailable = false;
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (isFollowersList) followersModels.addAll(result.getItems());
|
||||
else followingModels.addAll(result.getItems());
|
||||
refreshAdapter(followModels, null, null, null);
|
||||
layoutManager.scrollToPosition(oldSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Error fetching list (single)", t);
|
||||
}
|
||||
};
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
friendshipService.getList(isFollowersList, profileId, null, cb);
|
||||
layoutManager = new LinearLayoutManager(getContext());
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (!TextUtils.isEmpty(endCursor)) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
layoutManager.setStackFromEnd(true);
|
||||
friendshipService.getList(isFollowersList, profileId, endCursor, cb);
|
||||
}
|
||||
endCursor = null;
|
||||
});
|
||||
binding.rvFollow.addOnScrollListener(lazyLoader);
|
||||
binding.rvFollow.setLayoutManager(layoutManager);
|
||||
if (moreAvailable) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
friendshipService.getList(isFollowersList, profileId, endCursor, cb);
|
||||
}
|
||||
else {
|
||||
refreshAdapter(followModels, null, null, null);
|
||||
layoutManager.scrollToPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void listCompare() {
|
||||
layoutManager.setStackFromEnd(false);
|
||||
binding.rvFollow.clearOnScrollListeners();
|
||||
loading = true;
|
||||
setSubtitle(R.string.followers_compare);
|
||||
allFollowing.clear();
|
||||
followersModels.clear();
|
||||
followingModels.clear();
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> followingFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||
if (result != null) {
|
||||
followingModels.addAll(result.getItems());
|
||||
|
||||
if (result.isMoreAvailable()) {
|
||||
friendshipService.getList(false, profileId, result.getNextMaxId(), this);
|
||||
} else {
|
||||
showCompare();
|
||||
}
|
||||
} else binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Log.e(TAG, "Error fetching list (double, following)", t);
|
||||
}
|
||||
};
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> followersFetchCb = new ServiceCallback<FriendshipRepoListFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoListFetchResponse result) {
|
||||
if (result != null) {
|
||||
followersModels.addAll(result.getItems());
|
||||
if (result.isMoreAvailable()) {
|
||||
friendshipService.getList(true, profileId, result.getNextMaxId(), this);
|
||||
} else if (followingModels.size() == 0) {
|
||||
friendshipService.getList(false, profileId, null, followingFetchCb);
|
||||
} else {
|
||||
showCompare();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Log.e(TAG, "Error fetching list (double, follower)", t);
|
||||
}
|
||||
};
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
if (followersModels.size() == 0) {
|
||||
friendshipService.getList(true, profileId, null, followersFetchCb);
|
||||
if (moreAvailable) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
|
||||
friendshipService.getList(isFollowersList,
|
||||
profileId,
|
||||
endCursor,
|
||||
isFollowersList ? followersFetchCb : followingFetchCb);
|
||||
}
|
||||
else if (followingModels.size() == 0) {
|
||||
friendshipService.getList(false, profileId, null, followingFetchCb);
|
||||
else if (followersModels.size() == 0 || followingModels.size() == 0) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
Toast.makeText(getContext(), R.string.follower_start_compare, Toast.LENGTH_LONG).show();
|
||||
friendshipService.getList(!isFollowersList,
|
||||
profileId,
|
||||
null,
|
||||
isFollowersList ? followingFetchCb : followersFetchCb);
|
||||
}
|
||||
else showCompare();
|
||||
}
|
||||
@ -346,12 +381,12 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
final Context context = getContext();
|
||||
if (loading) Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_LONG).show();
|
||||
else if (isCompare) {
|
||||
listFollows();
|
||||
isCompare = !isCompare;
|
||||
listFollows();
|
||||
}
|
||||
else {
|
||||
listCompare();
|
||||
isCompare = !isCompare;
|
||||
listCompare();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -371,8 +406,7 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
|
||||
if (allFollowing != null && allFollowing.size() > 0)
|
||||
groups.add(new ExpandableGroup(resources.getString(R.string.followers_both_following), allFollowing));
|
||||
} else {
|
||||
final ExpandableGroup group = new ExpandableGroup(type, followModels);
|
||||
groups.add(group);
|
||||
groups.add(new ExpandableGroup(type, followModels));
|
||||
}
|
||||
adapter = new FollowAdapter(clickListener, groups);
|
||||
adapter.toggleGroup(0);
|
||||
|
@ -54,6 +54,7 @@ import awais.instagrabber.db.entities.Favorite;
|
||||
import awais.instagrabber.db.repositories.FavoriteRepository;
|
||||
import awais.instagrabber.db.repositories.RepositoryCallback;
|
||||
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.HashtagModel;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
@ -363,19 +364,27 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private void fetchHashtagModel() {
|
||||
stopCurrentExecutor();
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> {
|
||||
hashtagModel = result;
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (hashtagModel == null) {
|
||||
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), new FetchListener<HashtagModel>() {
|
||||
@Override
|
||||
public void onResult(final HashtagModel result) {
|
||||
hashtagModel = result;
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (hashtagModel == null) {
|
||||
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
setTitle();
|
||||
setHashtagDetails();
|
||||
setupPosts();
|
||||
fetchStories();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
setTitle();
|
||||
setHashtagDetails();
|
||||
setupPosts();
|
||||
fetchStories();
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
@ -410,9 +419,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
hashtagDetailsBinding.btnFollowTag.setClickable(true);
|
||||
if (!result) {
|
||||
Log.e(TAG, "onSuccess: result is false");
|
||||
Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
hashtagDetailsBinding.btnFollowTag.setText(R.string.unfollow);
|
||||
hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -435,9 +447,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
hashtagDetailsBinding.btnFollowTag.setClickable(true);
|
||||
if (!result) {
|
||||
Log.e(TAG, "onSuccess: result is false");
|
||||
Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
onRefresh();
|
||||
hashtagDetailsBinding.btnFollowTag.setText(R.string.follow);
|
||||
hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_24);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -462,8 +477,23 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
favoriteRepository.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
result.getId(),
|
||||
hashtag.substring(1),
|
||||
FavoriteType.HASHTAG,
|
||||
hashtagModel.getName(),
|
||||
hashtagModel.getSdProfilePic(),
|
||||
result.getDateAdded()
|
||||
), new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -492,18 +522,18 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
-1,
|
||||
0,
|
||||
hashtag.substring(1),
|
||||
FavoriteType.HASHTAG,
|
||||
hashtagModel.getName(),
|
||||
null,
|
||||
hashtagModel.getSdProfilePic(),
|
||||
new Date()
|
||||
), new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
hashtagDetailsBinding.favChip.setText(R.string.favorite_short);
|
||||
hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
showSnackbar(getString(R.string.added_to_favs));
|
||||
showSnackbar(getString(R.string.added_to_favs_short));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -513,7 +543,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}));
|
||||
hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic());
|
||||
final String postCount = String.valueOf(hashtagModel.getPostCount());
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount));
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
|
||||
hashtagModel.getPostCount() > 2000000000L ? 2000000000 : hashtagModel.getPostCount().intValue(),
|
||||
postCount));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
|
||||
hashtagDetailsBinding.mainTagPostCount.setText(span);
|
||||
@ -522,7 +554,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (!hasStories) return;
|
||||
// show stories
|
||||
final NavDirections action = HashTagFragmentDirections
|
||||
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
||||
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName(), false, false);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -400,8 +400,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
|
||||
final String postCount = String.valueOf(locationModel.getPostCount());
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
|
||||
postCount));
|
||||
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
|
||||
locationModel.getPostCount() > 2000000000L ? 2000000000 : locationModel.getPostCount().intValue(),
|
||||
postCount));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
|
||||
locationDetailsBinding.mainLocPostCount.setText(span);
|
||||
@ -455,6 +456,20 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
locationDetailsBinding.favChip.setText(R.string.favorite_short);
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
result.getId(),
|
||||
locationId,
|
||||
FavoriteType.LOCATION,
|
||||
locationModel.getName(),
|
||||
locationModel.getSdProfilePic(),
|
||||
result.getDateAdded()
|
||||
), new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -484,7 +499,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
-1,
|
||||
0,
|
||||
locationId,
|
||||
FavoriteType.LOCATION,
|
||||
locationModel.getName(),
|
||||
@ -495,7 +510,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
public void onSuccess(final Void result) {
|
||||
locationDetailsBinding.favChip.setText(R.string.favorite_short);
|
||||
locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
showSnackbar(getString(R.string.added_to_favs));
|
||||
showSnackbar(getString(R.string.added_to_favs_short));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -508,7 +523,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
if (hasStories) {
|
||||
// show stories
|
||||
final NavDirections action = LocationFragmentDirections
|
||||
.actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName());
|
||||
.actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName(), false, false);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
}
|
||||
});
|
||||
|
@ -18,20 +18,28 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
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.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.NotificationsAdapter;
|
||||
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
|
||||
import awais.instagrabber.asyncs.NotificationsFetcher;
|
||||
import awais.instagrabber.asyncs.PostFetcher;
|
||||
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
|
||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.interfaces.MentionClickListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
@ -40,6 +48,7 @@ import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.NotificationViewModel;
|
||||
import awais.instagrabber.webservices.FriendshipService;
|
||||
import awais.instagrabber.webservices.MediaService;
|
||||
import awais.instagrabber.webservices.NewsService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
@ -53,96 +62,146 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
private boolean shouldRefresh = true;
|
||||
private NotificationViewModel notificationViewModel;
|
||||
private FriendshipService friendshipService;
|
||||
private String userId;
|
||||
private String csrfToken;
|
||||
private MediaService mediaService;
|
||||
private NewsService newsService;
|
||||
private String userId, csrfToken, type;
|
||||
private Context context;
|
||||
|
||||
private final OnNotificationClickListener clickListener = model -> {
|
||||
if (model == null) return;
|
||||
final String username = model.getUsername();
|
||||
final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
|
||||
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
String[] commentDialogList;
|
||||
if (model.getShortCode() != null) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.view_post)
|
||||
};
|
||||
} else if (model.getType() == NotificationType.REQUEST) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.request_approve),
|
||||
getString(R.string.request_reject)
|
||||
};
|
||||
} else {
|
||||
commentDialogList = new String[]{getString(R.string.open_profile)};
|
||||
private final OnNotificationClickListener clickListener = new OnNotificationClickListener() {
|
||||
@Override
|
||||
public void onProfileClick(final String username) {
|
||||
openProfile(username);
|
||||
}
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0:
|
||||
openProfile(model.getUsername());
|
||||
break;
|
||||
case 1:
|
||||
if (model.getType() == NotificationType.REQUEST) {
|
||||
friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
// Log.d(TAG, "onSuccess: " + result);
|
||||
if (result.getStatus().equals("ok")) {
|
||||
onRefresh();
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "approve: status was not ok!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "approve: onFailure: ", t);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.dialog_opening_post)
|
||||
.create();
|
||||
alertDialog.show();
|
||||
new PostFetcher(model.getShortCode(), feedModel -> {
|
||||
@Override
|
||||
public void onPreviewClick(final NotificationModel model) {
|
||||
if (model.getType() == NotificationType.RESPONDED_STORY) {
|
||||
final NavDirections action = NotificationsViewerFragmentDirections.actionNotificationsViewerFragmentToStoryViewerFragment(
|
||||
-1, null, false, false, model.getPostId(), model.getUsername(), false, true);
|
||||
NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
|
||||
}
|
||||
else {
|
||||
mediaService.fetch(model.getPostId(), new ServiceCallback<FeedModel>() {
|
||||
@Override
|
||||
public void onSuccess(final FeedModel feedModel) {
|
||||
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||
.builder(feedModel)
|
||||
.build();
|
||||
fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
}).execute();
|
||||
break;
|
||||
case 2:
|
||||
friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
// Log.d(TAG, "onSuccess: " + result);
|
||||
if (result.getStatus().equals("ok")) {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationClick(final NotificationModel model) {
|
||||
if (model == null) return;
|
||||
final String username = model.getUsername();
|
||||
if (model.getType() == NotificationType.FOLLOW || model.getType() == NotificationType.AYML) {
|
||||
openProfile(username);
|
||||
}
|
||||
else {
|
||||
final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
|
||||
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
String[] commentDialogList;
|
||||
if (model.getType() == NotificationType.RESPONDED_STORY) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.view_story)
|
||||
};
|
||||
}
|
||||
else if (model.getPostId() != null) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.view_post)
|
||||
};
|
||||
}
|
||||
else if (model.getType() == NotificationType.REQUEST) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.request_approve),
|
||||
getString(R.string.request_reject)
|
||||
};
|
||||
}
|
||||
else commentDialogList = null; // shouldn't happen
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0:
|
||||
openProfile(username);
|
||||
break;
|
||||
case 1:
|
||||
if (model.getType() == NotificationType.REQUEST) {
|
||||
friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
onRefresh();
|
||||
Log.e(TAG, "approve: status was not ok!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "approve: onFailure: ", t);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "ignore: status was not ok!");
|
||||
}
|
||||
else if (model.getType() == NotificationType.RESPONDED_STORY) {
|
||||
final NavDirections action = NotificationsViewerFragmentDirections.actionNotificationsViewerFragmentToStoryViewerFragment(
|
||||
-1, null, false, false, model.getPostId(), model.getUsername(), false, true);
|
||||
NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
|
||||
return;
|
||||
}
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.dialog_opening_post)
|
||||
.create();
|
||||
alertDialog.show();
|
||||
mediaService.fetch(model.getPostId(), new ServiceCallback<FeedModel>() {
|
||||
@Override
|
||||
public void onSuccess(final FeedModel feedModel) {
|
||||
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||
.builder(feedModel)
|
||||
.build();
|
||||
fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "ignore: onFailure: ", t);
|
||||
}
|
||||
});
|
||||
break;
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "ignore: onFailure: ", t);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(title)
|
||||
.setItems(commentDialogList, profileDialogListener)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
};
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(title)
|
||||
.setItems(commentDialogList, profileDialogListener)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
};
|
||||
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
||||
if (getContext() == null) return;
|
||||
@ -158,7 +217,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Context context = getContext();
|
||||
context = getContext();
|
||||
if (context == null) return;
|
||||
NotificationManagerCompat.from(context.getApplicationContext()).cancel(Constants.ACTIVITY_NOTIFICATION_ID);
|
||||
final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
|
||||
@ -166,7 +225,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
Toast.makeText(context, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
friendshipService = FriendshipService.getInstance();
|
||||
newsService = NewsService.getInstance();
|
||||
mediaService = MediaService.getInstance();
|
||||
userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
}
|
||||
@ -191,6 +250,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final NotificationsViewerFragmentArgs fragmentArgs = NotificationsViewerFragmentArgs.fromBundle(getArguments());
|
||||
type = fragmentArgs.getType();
|
||||
final Context context = getContext();
|
||||
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE));
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
@ -205,23 +266,39 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
new NotificationsFetcher(notificationModels -> {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
final String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
newsService.markChecked(timestamp, csrfToken, new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(@NonNull final Boolean result) {
|
||||
// Log.d(TAG, "onResponse: body: " + result);
|
||||
if (!result) Log.e(TAG, "onSuccess: Error marking activity checked, response is false");
|
||||
}
|
||||
switch (type) {
|
||||
case "notif":
|
||||
new NotificationsFetcher(true, new FetchListener<List<NotificationModel>>() {
|
||||
@Override
|
||||
public void onResult(final List<NotificationModel> notificationModels) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: Error marking activity checked", t);
|
||||
}
|
||||
});
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
break;
|
||||
case "ayml":
|
||||
newsService = NewsService.getInstance();
|
||||
newsService.fetchSuggestions(csrfToken, new ServiceCallback<List<NotificationModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<NotificationModel> notificationModels) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void openProfile(final String username) {
|
||||
|
@ -26,6 +26,7 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Toast;
|
||||
@ -33,6 +34,7 @@ import android.widget.ViewSwitcher;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.PermissionChecker;
|
||||
import androidx.core.view.ViewCompat;
|
||||
@ -70,6 +72,7 @@ import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
|
||||
import awais.instagrabber.customviews.VideoPlayerViewHelper;
|
||||
import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
|
||||
import awais.instagrabber.databinding.DialogPostViewBinding;
|
||||
import awais.instagrabber.fragments.main.ProfileFragment;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostChild;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
@ -114,6 +117,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
private DialogInterface.OnShowListener onShowListener;
|
||||
private boolean isLoggedIn;
|
||||
private boolean hasBeenToggled = false;
|
||||
private CharSequence postCaption = null;
|
||||
|
||||
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() {
|
||||
|
||||
@ -306,6 +310,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (feedModel == null) return;
|
||||
switch (feedModel.getItemType()) {
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
if (videoPlayerViewHelper != null) {
|
||||
@ -414,8 +419,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
|
||||
private void init() {
|
||||
if (feedModel == null) return;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
isLoggedIn = !TextUtils.isEmpty(COOKIE) && CookieUtils.getUserIdFromCookie(COOKIE) != null;
|
||||
if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
|
||||
binding.getRoot().getBackground().mutate().setAlpha(0);
|
||||
}
|
||||
@ -534,7 +538,16 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
}
|
||||
});
|
||||
binding.like.setOnLongClickListener(v -> {
|
||||
Utils.displayToastAboveView(context, v, getString(R.string.like_without_count));
|
||||
final NavController navController = getNavController();
|
||||
if (navController != null && isLoggedIn) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("postId", feedModel.getPostId());
|
||||
bundle.putBoolean("isComment", false);
|
||||
navController.navigate(R.id.action_global_likesViewerFragment, bundle);
|
||||
}
|
||||
else {
|
||||
Utils.displayToastAboveView(context, v, getString(R.string.like_without_count));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -701,13 +714,52 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
}
|
||||
|
||||
private void setupCaption() {
|
||||
final CharSequence postCaption = feedModel.getPostCaption();
|
||||
postCaption = feedModel.getPostCaption();
|
||||
binding.date.setText(Utils.datetimeParser.format(new Date(feedModel.getTimestamp() * 1000L)));
|
||||
if (TextUtils.isEmpty(postCaption)) {
|
||||
if (!feedModel.getProfileModel().getId().equals(CookieUtils.getUserIdFromCookie(COOKIE)) && TextUtils.isEmpty(postCaption)) {
|
||||
binding.caption.setVisibility(View.GONE);
|
||||
binding.translateTitle.setVisibility(View.GONE);
|
||||
binding.captionToggle.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
if (feedModel.getProfileModel().getId().equals(CookieUtils.getUserIdFromCookie(COOKIE))) {
|
||||
binding.editCaption.setVisibility(View.VISIBLE);
|
||||
binding.editCaption.setOnClickListener(v -> {
|
||||
final EditText input = new EditText(context);
|
||||
input.setText(postCaption);
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.edit_caption)
|
||||
.setView(input)
|
||||
.setPositiveButton(R.string.confirm, (d, w) -> {
|
||||
binding.editCaption.setVisibility(View.GONE);
|
||||
mediaService.editCaption(
|
||||
feedModel.getPostId(),
|
||||
CookieUtils.getUserIdFromCookie(COOKIE),
|
||||
input.getText().toString(),
|
||||
CookieUtils.getCsrfTokenFromCookie(COOKIE),
|
||||
new ServiceCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(final Boolean result) {
|
||||
binding.editCaption.setVisibility(View.VISIBLE);
|
||||
if (result) {
|
||||
feedModel.setPostCaption(input.getText().toString());
|
||||
binding.caption.setText(input.getText().toString());
|
||||
}
|
||||
else Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error editing caption", t);
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
binding.editCaption.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
binding.caption.addOnHashtagListener(autoLinkItem -> {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final Bundle bundle = new Bundle();
|
||||
@ -737,11 +789,33 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
binding.captionParent.getBackground().mutate().setAlpha((int) (128 + (128 * (slideOffset < 0 ? 0 : slideOffset))));
|
||||
}
|
||||
});
|
||||
binding.caption.setOnClickListener(v -> {
|
||||
binding.captionFrame.setOnClickListener(v -> {
|
||||
if (bottomSheetBehavior == null) return;
|
||||
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) return;
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
});
|
||||
if (TextUtils.isEmpty(feedModel.getCaptionId()))
|
||||
binding.translateTitle.setVisibility(View.GONE);
|
||||
else binding.translateTitle.setOnClickListener(v -> {
|
||||
mediaService.translate(feedModel.getCaptionId(), "1", new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
if (TextUtils.isEmpty(result)) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
binding.translateTitle.setOnClickListener(null);
|
||||
binding.translatedCaption.setVisibility(View.VISIBLE);
|
||||
binding.translatedCaption.setText(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error translating comment", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
});
|
||||
binding.captionToggle.setOnClickListener(v -> {
|
||||
if (bottomSheetBehavior == null) return;
|
||||
switch (bottomSheetBehavior.getState()) {
|
||||
@ -894,6 +968,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
|
||||
@Override
|
||||
public void onItemClicked(final int position) {
|
||||
toggleDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,24 +40,26 @@ import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||
|
||||
private FragmentSavedBinding binding;
|
||||
private String username;
|
||||
private String username, cookie, profileId;
|
||||
private ActionMode actionMode;
|
||||
private SwipeRefreshLayout root;
|
||||
private AppCompatActivity fragmentActivity;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean isLoggedIn, shouldRefresh = true;
|
||||
private PostItemType type;
|
||||
private String profileId;
|
||||
private Set<FeedModel> selectedFeedModels;
|
||||
private FeedModel downloadFeedModel;
|
||||
private int downloadChildPosition = -1;
|
||||
@ -225,6 +227,8 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
@ -281,7 +285,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
private void setupPosts() {
|
||||
binding.posts.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new SavedPostFetchService(profileId, type))
|
||||
.setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn))
|
||||
.setLayoutPreferences(layoutPreferences)
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@ -20,6 +21,9 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -54,6 +58,9 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -62,10 +69,7 @@ import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.StoriesAdapter;
|
||||
import awais.instagrabber.asyncs.PostFetcher;
|
||||
import awais.instagrabber.asyncs.QuizAction;
|
||||
import awais.instagrabber.asyncs.RespondAction;
|
||||
import awais.instagrabber.asyncs.SeenAction;
|
||||
import awais.instagrabber.asyncs.VoteAction;
|
||||
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
||||
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
|
||||
import awais.instagrabber.databinding.FragmentStoryViewerBinding;
|
||||
@ -78,16 +82,27 @@ import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.models.stickers.PollModel;
|
||||
import awais.instagrabber.models.stickers.QuestionModel;
|
||||
import awais.instagrabber.models.stickers.QuizModel;
|
||||
import awais.instagrabber.models.stickers.SliderModel;
|
||||
import awais.instagrabber.models.stickers.SwipeUpModel;
|
||||
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions;
|
||||
import awais.instagrabber.repositories.responses.StoryStickerResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.ArchivesViewModel;
|
||||
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
|
||||
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
||||
import awais.instagrabber.viewmodels.StoriesViewModel;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awais.instagrabber.webservices.StoriesService;
|
||||
import awaisomereport.LogCollector;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
|
||||
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
|
||||
@ -100,8 +115,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
|
||||
private AppCompatActivity fragmentActivity;
|
||||
private View root;
|
||||
private @NonNull
|
||||
FragmentStoryViewerBinding binding;
|
||||
private FragmentStoryViewerBinding binding;
|
||||
private String currentStoryUsername;
|
||||
private StoriesAdapter storiesAdapter;
|
||||
private SwipeEvent swipeEvent;
|
||||
@ -115,26 +129,32 @@ public class StoryViewerFragment extends Fragment {
|
||||
private QuestionModel question;
|
||||
private String[] mentions;
|
||||
private QuizModel quiz;
|
||||
private SliderModel slider;
|
||||
private MenuItem menuDownload;
|
||||
private MenuItem menuDm;
|
||||
private SimpleExoPlayer player;
|
||||
private boolean isHashtag, isLoc;
|
||||
private String highlight;
|
||||
private boolean fetching = false;
|
||||
private boolean fetching = false, sticking = false, shouldRefresh = true;
|
||||
private int currentFeedStoryIndex;
|
||||
private double sliderValue;
|
||||
private StoriesViewModel storiesViewModel;
|
||||
private boolean shouldRefresh = true;
|
||||
private StoryViewerFragmentArgs fragmentArgs;
|
||||
private ViewModel viewModel;
|
||||
private boolean isHighlight;
|
||||
private boolean isHighlight, isArchive, isNotification;
|
||||
private DirectMessagesService directMessagesService;
|
||||
|
||||
private final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
private final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
private final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
private final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (AppCompatActivity) requireActivity();
|
||||
storiesService = StoriesService.getInstance();
|
||||
directMessagesService = DirectMessagesService.getInstance(csrfToken, userIdFromCookie, deviceId);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@ -188,24 +208,33 @@ public class StoryViewerFragment extends Fragment {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.reply_story)
|
||||
.setView(input)
|
||||
.setPositiveButton(R.string.ok, (d, w) -> new CreateThreadAction(cookie, currentStory.getUserId(), threadId -> {
|
||||
// try {
|
||||
// final StoryReplyBroadcastOptions options = new StoryReplyBroadcastOptions(
|
||||
// threadId,
|
||||
// input.getText().toString(),
|
||||
// currentStory.getStoryMediaId(),
|
||||
// currentStory.getUserId()
|
||||
// );
|
||||
// final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadId);
|
||||
// broadcast.setOnTaskCompleteListener(result -> Toast.makeText(
|
||||
// context,
|
||||
// result != null ? R.string.answered_story : R.string.downloader_unknown_error,
|
||||
// Toast.LENGTH_SHORT
|
||||
// ).show());
|
||||
// broadcast.execute(options);
|
||||
// } catch (UnsupportedEncodingException e) {
|
||||
// Log.e(TAG, "Error", e);
|
||||
// }
|
||||
.setPositiveButton(R.string.confirm, (d, w) -> new CreateThreadAction(cookie, currentStory.getUserId(), threadId -> {
|
||||
try {
|
||||
final Call<DirectThreadBroadcastResponse> request = directMessagesService
|
||||
.broadcastStoryReply(BroadcastOptions.ThreadIdOrUserIds.of(threadId),
|
||||
input.getText().toString(),
|
||||
currentStory.getStoryMediaId(),
|
||||
currentStory.getUserId());
|
||||
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectThreadBroadcastResponse> call,
|
||||
@NonNull final Response<DirectThreadBroadcastResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectThreadBroadcastResponse> call, @NonNull final Throwable t) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
}
|
||||
}).execute())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
@ -244,9 +273,13 @@ public class StoryViewerFragment extends Fragment {
|
||||
currentFeedStoryIndex = fragmentArgs.getFeedStoryIndex();
|
||||
highlight = fragmentArgs.getHighlight();
|
||||
isHighlight = !TextUtils.isEmpty(highlight);
|
||||
isArchive = fragmentArgs.getIsArchive();
|
||||
isNotification = fragmentArgs.getIsNotification();
|
||||
if (currentFeedStoryIndex >= 0) {
|
||||
viewModel = isHighlight
|
||||
? new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class)
|
||||
? isArchive
|
||||
? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class)
|
||||
: new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class)
|
||||
: new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
|
||||
}
|
||||
// feedStoryModels = feedStoriesViewModel.getList().getValue();
|
||||
@ -275,7 +308,10 @@ public class StoryViewerFragment extends Fragment {
|
||||
final boolean hasFeedStories;
|
||||
List<?> models = null;
|
||||
if (currentFeedStoryIndex >= 0) {
|
||||
if (isHighlight) {
|
||||
if (isArchive) {
|
||||
final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel;
|
||||
models = archivesViewModel.getList().getValue();
|
||||
} else if (isHighlight) {
|
||||
final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel;
|
||||
models = highlightsViewModel.getList().getValue();
|
||||
// final HighlightModel model = models.get(currentFeedStoryIndex);
|
||||
@ -296,15 +332,16 @@ public class StoryViewerFragment extends Fragment {
|
||||
swipeEvent = isRightSwipe -> {
|
||||
final List<StoryModel> storyModels = storiesViewModel.getList().getValue();
|
||||
final int storiesLen = storyModels == null ? 0 : storyModels.size();
|
||||
if (sticking) {
|
||||
Toast.makeText(context, R.string.follower_wait_to_load, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (storiesLen <= 0) return;
|
||||
final boolean isLeftSwipe = !isRightSwipe;
|
||||
final boolean endOfCurrentStories = slidePos + 1 >= storiesLen;
|
||||
final boolean swipingBeyondCurrentStories = (endOfCurrentStories && isLeftSwipe) || (slidePos == 0 && isRightSwipe);
|
||||
if (swipingBeyondCurrentStories && hasFeedStories) {
|
||||
final int index = currentFeedStoryIndex;
|
||||
if (settingsHelper.getBoolean(MARK_AS_SEEN)) {
|
||||
new SeenAction(cookie, currentStory).execute();
|
||||
}
|
||||
if ((isRightSwipe && index == 0) || (isLeftSwipe && index == finalModels.size() - 1)) {
|
||||
Toast.makeText(context, R.string.no_more_stories, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@ -312,7 +349,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
final Object feedStoryModel = isRightSwipe
|
||||
? finalModels.get(index - 1)
|
||||
: finalModels.size() == index + 1 ? null : finalModels.get(index + 1);
|
||||
paginateStories(feedStoryModel, context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
|
||||
paginateStories(feedStoryModel, finalModels.get(index), context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
|
||||
return;
|
||||
}
|
||||
if (isRightSwipe) {
|
||||
@ -351,8 +388,12 @@ public class StoryViewerFragment extends Fragment {
|
||||
if (hasFeedStories) {
|
||||
binding.btnBackward.setVisibility(currentFeedStoryIndex == 0 ? View.INVISIBLE : View.VISIBLE);
|
||||
binding.btnForward.setVisibility(currentFeedStoryIndex == finalModels.size() - 1 ? View.INVISIBLE : View.VISIBLE);
|
||||
binding.btnBackward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex - 1), context, true, false));
|
||||
binding.btnForward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex + 1), context, false,
|
||||
binding.btnBackward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex - 1),
|
||||
finalModels.get(currentFeedStoryIndex),
|
||||
context, true, false));
|
||||
binding.btnForward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex + 1),
|
||||
finalModels.get(currentFeedStoryIndex),
|
||||
context, false,
|
||||
currentFeedStoryIndex == finalModels.size() - 2));
|
||||
}
|
||||
|
||||
@ -365,6 +406,14 @@ public class StoryViewerFragment extends Fragment {
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
binding.swipeUp.setOnClickListener(v -> {
|
||||
final Object tag = v.getTag();
|
||||
if (tag instanceof CharSequence) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(tag.toString()));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
binding.viewStoryPost.setOnClickListener(v -> {
|
||||
final Object tag = v.getTag();
|
||||
if (!(tag instanceof CharSequence)) return;
|
||||
@ -406,15 +455,30 @@ public class StoryViewerFragment extends Fragment {
|
||||
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
|
||||
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
|
||||
}), (d, w) -> {
|
||||
if (!TextUtils.isEmpty(cookie))
|
||||
new VoteAction(currentStory, poll, cookie, choice -> {
|
||||
if (choice > -1) {
|
||||
poll.setMyChoice(choice);
|
||||
Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}).execute(w);
|
||||
if (!TextUtils.isEmpty(cookie)) {
|
||||
sticking = true;
|
||||
storiesService.respondToPoll(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
poll.getId(),
|
||||
w,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
sticking = false;
|
||||
poll.setMyChoice(w);
|
||||
Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
sticking = false;
|
||||
Log.e(TAG, "Error responding", t);
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.cancel, null)
|
||||
.show();
|
||||
@ -426,21 +490,36 @@ public class StoryViewerFragment extends Fragment {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(question.getQuestion())
|
||||
.setView(input)
|
||||
.setPositiveButton(R.string.ok, (d, w) -> new RespondAction(currentStory, question, cookie, result -> {
|
||||
if (result) {
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
} else
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}).execute(input.getText().toString()))
|
||||
.setPositiveButton(R.string.confirm, (d, w) -> {
|
||||
sticking = true;
|
||||
storiesService.respondToQuestion(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
question.getId(),
|
||||
input.getText().toString(),
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
sticking = false;
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
sticking = false;
|
||||
Log.e(TAG, "Error responding", t);
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
} else if (tag instanceof String[]) {
|
||||
mentions = (String[]) tag;
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.story_mentions)
|
||||
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> {
|
||||
openProfile(mentions[w]);
|
||||
})
|
||||
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, mentions), (d, w) -> openProfile(mentions[w]))
|
||||
.setPositiveButton(R.string.cancel, null)
|
||||
.show();
|
||||
} else if (tag instanceof QuizModel) {
|
||||
@ -451,27 +530,122 @@ public class StoryViewerFragment extends Fragment {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
|
||||
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> {
|
||||
if (quiz.getMyChoice() == -1 && !TextUtils.isEmpty(cookie))
|
||||
new QuizAction(currentStory, quiz, cookie, choice -> {
|
||||
if (choice > -1) {
|
||||
quiz.setMyChoice(choice);
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}).execute(w);
|
||||
if (quiz.getMyChoice() == -1 && !TextUtils.isEmpty(cookie)) {
|
||||
sticking = true;
|
||||
storiesService.respondToQuiz(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
quiz.getId(),
|
||||
w,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
sticking = false;
|
||||
quiz.setMyChoice(w);
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
sticking = false;
|
||||
Log.e(TAG, "Error responding", t);
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.cancel, null)
|
||||
.show();
|
||||
} else if (tag instanceof SliderModel) {
|
||||
slider = (SliderModel) tag;
|
||||
NumberFormat percentage = NumberFormat.getPercentInstance();
|
||||
percentage.setMaximumFractionDigits(2);
|
||||
LinearLayout sliderView = new LinearLayout(context);
|
||||
sliderView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
sliderView.setOrientation(LinearLayout.VERTICAL);
|
||||
TextView tv = new TextView(context);
|
||||
tv.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
final SeekBar input = new SeekBar(context);
|
||||
double avg = slider.getAverage() * 100;
|
||||
input.setProgress((int) avg);
|
||||
sliderView.addView(input);
|
||||
sliderView.addView(tv);
|
||||
if (slider.getMyChoice().isNaN() && slider.canVote()) {
|
||||
input.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
sliderValue = progress / 100.0;
|
||||
tv.setText(percentage.format(sliderValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
|
||||
.setMessage(getResources().getQuantityString(R.plurals.slider_info,
|
||||
slider.getVoteCount(),
|
||||
slider.getVoteCount(),
|
||||
percentage.format(slider.getAverage())))
|
||||
.setView(sliderView)
|
||||
.setPositiveButton(R.string.confirm, (d, w) -> {
|
||||
sticking = true;
|
||||
storiesService.respondToSlider(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
slider.getId(),
|
||||
sliderValue,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
sticking = false;
|
||||
slider.setMyChoice(sliderValue);
|
||||
Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
sticking = false;
|
||||
Log.e(TAG, "Error responding", t);
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
} else {
|
||||
input.setEnabled(false);
|
||||
tv.setText(getString(R.string.slider_answer, percentage.format(slider.getMyChoice())));
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(TextUtils.isEmpty(slider.getQuestion()) ? slider.getEmoji() : slider.getQuestion())
|
||||
.setMessage(getResources().getQuantityString(R.plurals.slider_info,
|
||||
slider.getVoteCount(),
|
||||
slider.getVoteCount(),
|
||||
percentage.format(slider.getAverage())))
|
||||
.setView(sliderView)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
binding.poll.setOnClickListener(storyActionListener);
|
||||
binding.answer.setOnClickListener(storyActionListener);
|
||||
binding.mention.setOnClickListener(storyActionListener);
|
||||
binding.quiz.setOnClickListener(storyActionListener);
|
||||
binding.slider.setOnClickListener(storyActionListener);
|
||||
}
|
||||
|
||||
private void resetView() {
|
||||
final Context context = getContext();
|
||||
slidePos = 0;
|
||||
lastSlidePos = 0;
|
||||
if (menuDownload != null) menuDownload.setVisible(false);
|
||||
@ -480,10 +654,23 @@ public class StoryViewerFragment extends Fragment {
|
||||
releasePlayer();
|
||||
String currentStoryMediaId = null;
|
||||
if (currentFeedStoryIndex >= 0) {
|
||||
if (isHighlight) {
|
||||
if (isArchive) {
|
||||
final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel;
|
||||
final List<HighlightModel> models = archivesViewModel.getList().getValue();
|
||||
if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
final HighlightModel model = models.get(currentFeedStoryIndex);
|
||||
currentStoryMediaId = model.getId();
|
||||
currentStoryUsername = model.getTitle();
|
||||
} else if (isHighlight) {
|
||||
final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel;
|
||||
final List<HighlightModel> models = highlightsViewModel.getList().getValue();
|
||||
if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) return;
|
||||
if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
final HighlightModel model = models.get(currentFeedStoryIndex);
|
||||
currentStoryMediaId = model.getId();
|
||||
currentStoryUsername = model.getTitle();
|
||||
@ -502,7 +689,12 @@ public class StoryViewerFragment extends Fragment {
|
||||
isHashtag = fragmentArgs.getIsHashtag();
|
||||
isLoc = fragmentArgs.getIsLoc();
|
||||
final boolean hasUsername = !TextUtils.isEmpty(currentStoryUsername);
|
||||
if (hasUsername) {
|
||||
if (isHighlight) {
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(highlight);
|
||||
}
|
||||
} else if (hasUsername) {
|
||||
currentStoryUsername = currentStoryUsername.replace("@", "");
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
@ -511,6 +703,31 @@ public class StoryViewerFragment extends Fragment {
|
||||
}
|
||||
storiesViewModel.getList().setValue(Collections.emptyList());
|
||||
if (currentStoryMediaId == null) return;
|
||||
if (isNotification) {
|
||||
storiesService.fetch(currentStoryMediaId, new ServiceCallback<StoryModel>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryModel storyModel) {
|
||||
fetching = false;
|
||||
binding.storiesList.setVisibility(View.GONE);
|
||||
if (storyModel == null) {
|
||||
storiesViewModel.getList().setValue(Collections.emptyList());
|
||||
currentStory = null;
|
||||
return;
|
||||
}
|
||||
storiesViewModel.getList().setValue(Collections.singletonList(storyModel));
|
||||
currentStory = storyModel;
|
||||
refreshStory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
final Context context = getContext();
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Error", t);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> storyModels) {
|
||||
@ -521,7 +738,9 @@ public class StoryViewerFragment extends Fragment {
|
||||
binding.storiesList.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
binding.storiesList.setVisibility(View.VISIBLE);
|
||||
binding.storiesList.setVisibility((storyModels.size() == 1 && currentFeedStoryIndex == -1) ? View.GONE : View.VISIBLE);
|
||||
binding.btnBackward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE);
|
||||
binding.btnForward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE);
|
||||
storiesViewModel.getList().setValue(storyModels);
|
||||
currentStory = storyModels.get(0);
|
||||
refreshStory();
|
||||
@ -529,6 +748,8 @@ public class StoryViewerFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
final Context context = getContext();
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(TAG, "Error", t);
|
||||
}
|
||||
};
|
||||
@ -543,7 +764,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
private void refreshStory() {
|
||||
if (binding.storiesList.getVisibility() == View.VISIBLE) {
|
||||
final List<StoryModel> storyModels = storiesViewModel.getList().getValue();
|
||||
if (storyModels != null) {
|
||||
if (storyModels != null && storyModels.size() > 0) {
|
||||
StoryModel item = storyModels.get(lastSlidePos);
|
||||
if (item != null) {
|
||||
item.setCurrentSlide(false);
|
||||
@ -587,6 +808,17 @@ public class StoryViewerFragment extends Fragment {
|
||||
binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
|
||||
binding.quiz.setTag(quiz);
|
||||
|
||||
slider = currentStory.getSlider();
|
||||
binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE);
|
||||
binding.slider.setTag(slider);
|
||||
|
||||
final SwipeUpModel swipeUp = currentStory.getSwipeUp();
|
||||
if (swipeUp != null) {
|
||||
binding.swipeUp.setVisibility(View.VISIBLE);
|
||||
binding.swipeUp.setText(swipeUp.getText());
|
||||
binding.swipeUp.setTag(swipeUp.getUrl());
|
||||
}
|
||||
|
||||
releasePlayer();
|
||||
if (isHashtag || isLoc) {
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
@ -670,8 +902,8 @@ public class StoryViewerFragment extends Fragment {
|
||||
@Override
|
||||
public void onLoadCompleted(final int windowIndex,
|
||||
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
|
||||
final LoadEventInfo loadEventInfo,
|
||||
final MediaLoadData mediaLoadData) {
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData) {
|
||||
if (menuDownload != null) menuDownload.setVisible(true);
|
||||
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
|
||||
menuDm.setVisible(true);
|
||||
@ -681,8 +913,8 @@ public class StoryViewerFragment extends Fragment {
|
||||
@Override
|
||||
public void onLoadStarted(final int windowIndex,
|
||||
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
|
||||
final LoadEventInfo loadEventInfo,
|
||||
final MediaLoadData mediaLoadData) {
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData) {
|
||||
if (menuDownload != null) menuDownload.setVisible(true);
|
||||
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
|
||||
menuDm.setVisible(true);
|
||||
@ -692,17 +924,17 @@ public class StoryViewerFragment extends Fragment {
|
||||
@Override
|
||||
public void onLoadCanceled(final int windowIndex,
|
||||
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
|
||||
final LoadEventInfo loadEventInfo,
|
||||
final MediaLoadData mediaLoadData) {
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData) {
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadError(final int windowIndex,
|
||||
@Nullable final MediaSource.MediaPeriodId mediaPeriodId,
|
||||
final LoadEventInfo loadEventInfo,
|
||||
final MediaLoadData mediaLoadData,
|
||||
final IOException error,
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData,
|
||||
@NonNull final IOException error,
|
||||
final boolean wasCanceled) {
|
||||
if (menuDownload != null) menuDownload.setVisible(false);
|
||||
if (menuDm != null) menuDm.setVisible(false);
|
||||
@ -745,12 +977,25 @@ public class StoryViewerFragment extends Fragment {
|
||||
player = null;
|
||||
}
|
||||
|
||||
private void paginateStories(Object feedStory, Context context, boolean backward, boolean last) {
|
||||
if (feedStory != null) {
|
||||
private void paginateStories(Object newFeedStory, Object oldFeedStory, Context context, boolean backward, boolean last) {
|
||||
if (newFeedStory != null) {
|
||||
if (fetching) {
|
||||
Toast.makeText(context, R.string.be_patient, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (settingsHelper.getBoolean(MARK_AS_SEEN)
|
||||
&& oldFeedStory instanceof FeedStoryModel
|
||||
&& viewModel instanceof FeedStoriesViewModel) {
|
||||
final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel;
|
||||
final FeedStoryModel oldFeedStoryModel = (FeedStoryModel) oldFeedStory;
|
||||
if (!oldFeedStoryModel.isFullyRead()) {
|
||||
oldFeedStoryModel.setFullyRead(true);
|
||||
final List<FeedStoryModel> models = feedStoriesViewModel.getList().getValue();
|
||||
final List<FeedStoryModel> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
|
||||
modelsCopy.set(currentFeedStoryIndex, oldFeedStoryModel);
|
||||
feedStoriesViewModel.getList().postValue(models);
|
||||
}
|
||||
}
|
||||
fetching = true;
|
||||
binding.btnBackward.setVisibility(currentFeedStoryIndex == 1 && backward ? View.INVISIBLE : View.VISIBLE);
|
||||
binding.btnForward.setVisibility(last ? View.INVISIBLE : View.VISIBLE);
|
||||
|
@ -18,7 +18,9 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@ -42,6 +44,7 @@ import awais.instagrabber.utils.Utils;
|
||||
public class DirectMessageSettingsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "DirectMsgsSettingsFrag";
|
||||
|
||||
private AppCompatActivity fragmentActivity;
|
||||
private RecyclerView userList;
|
||||
private RecyclerView leftUserList;
|
||||
private EditText titleText;
|
||||
@ -82,6 +85,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (AppCompatActivity) requireActivity();
|
||||
basicClickListener = v -> {
|
||||
final Object tag = v.getTag();
|
||||
if (tag instanceof ProfileModel) {
|
||||
@ -147,6 +151,11 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
|
||||
threadTitle = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getTitle();
|
||||
binding.swipeRefreshLayout.setEnabled(false);
|
||||
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(threadTitle);
|
||||
}
|
||||
|
||||
userList = binding.userList;
|
||||
userList.setHasFixedSize(true);
|
||||
userList.setLayoutManager(layoutManager);
|
||||
@ -209,7 +218,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
|
||||
class ChangeSettings extends AsyncTask<String, Void, Void> {
|
||||
String action, argument;
|
||||
boolean ok = false;
|
||||
private String text;
|
||||
private final String text;
|
||||
|
||||
public ChangeSettings(final String text) {
|
||||
this.text = text;
|
||||
|
@ -12,6 +12,7 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.activity.OnBackPressedDispatcher;
|
||||
@ -47,6 +48,7 @@ import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.FeedStoryModel;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
|
||||
@ -55,6 +57,7 @@ import awais.instagrabber.webservices.StoriesService;
|
||||
|
||||
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "FeedFragment";
|
||||
@ -74,6 +77,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
private int downloadChildPosition = -1;
|
||||
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_POSTS_LAYOUT);
|
||||
private RecyclerView storiesRecyclerView;
|
||||
private MenuItem storyListMenu;
|
||||
|
||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||
@Override
|
||||
@ -272,26 +276,23 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.feed_menu, menu);
|
||||
storyListMenu = menu.findItem(R.id.storyList);
|
||||
storyListMenu.setVisible(!storiesFetching);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (item.getItemId() == R.id.layout) {
|
||||
if (item.getItemId() == R.id.storyList) {
|
||||
final NavDirections action = FeedFragmentDirections.actionGlobalStoryListViewerFragment("feed");
|
||||
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
|
||||
}
|
||||
else if (item.getItemId() == R.id.layout) {
|
||||
showPostsLayoutPreferences();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateSwipeRefreshState();
|
||||
// if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
|
||||
// videoAwareRecyclerScroller.startPlaying();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
binding.feedRecyclerView.refresh();
|
||||
@ -309,9 +310,13 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
final boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (!granted) {
|
||||
Toast.makeText(context, R.string.download_permission, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||
if (downloadFeedModel == null) return;
|
||||
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||
@ -346,11 +351,22 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
}
|
||||
|
||||
private void setupFeedStories() {
|
||||
if (storyListMenu != null) storyListMenu.setVisible(false);
|
||||
feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
|
||||
final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter((model, position) -> {
|
||||
final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(
|
||||
new FeedStoriesAdapter.OnFeedStoryClickListener() {
|
||||
@Override
|
||||
public void onFeedStoryClick(FeedStoryModel model, int position) {
|
||||
final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false);
|
||||
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFeedStoryLongClick(FeedStoryModel model, int position) {
|
||||
navigateToProfile("@" + model.getProfileModel().getUsername());
|
||||
}
|
||||
}
|
||||
);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
storiesRecyclerView = new RecyclerView(context);
|
||||
@ -362,18 +378,20 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));
|
||||
storiesRecyclerView.setAdapter(feedStoriesAdapter);
|
||||
fragmentActivity.setCollapsingView(storiesRecyclerView);
|
||||
feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList);
|
||||
feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList);
|
||||
fetchStories();
|
||||
}
|
||||
|
||||
private void fetchStories() {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
storiesFetching = true;
|
||||
updateSwipeRefreshState();
|
||||
storiesService.getFeedStories(new ServiceCallback<List<FeedStoryModel>>() {
|
||||
storiesService.getFeedStories(CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<List<FeedStoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<FeedStoryModel> result) {
|
||||
feedStoriesViewModel.getList().postValue(result);
|
||||
storiesFetching = false;
|
||||
if (storyListMenu != null) storyListMenu.setVisible(true);
|
||||
updateSwipeRefreshState();
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,6 @@ import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||
import awais.instagrabber.adapters.HighlightsAdapter;
|
||||
import awais.instagrabber.asyncs.HighlightsFetcher;
|
||||
import awais.instagrabber.asyncs.ProfileFetcher;
|
||||
import awais.instagrabber.asyncs.ProfilePostFetchService;
|
||||
import awais.instagrabber.asyncs.UsernameFetcher;
|
||||
@ -75,6 +74,7 @@ import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.HighlightModel;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
@ -89,6 +89,7 @@ import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
||||
import awais.instagrabber.webservices.FriendshipService;
|
||||
import awais.instagrabber.webservices.MediaService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awais.instagrabber.webservices.StoriesService;
|
||||
|
||||
@ -113,6 +114,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private Handler usernameSettingHandler;
|
||||
private FriendshipService friendshipService;
|
||||
private StoriesService storiesService;
|
||||
private MediaService mediaService;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean hasStories = false;
|
||||
private HighlightsAdapter highlightsAdapter;
|
||||
@ -298,6 +300,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
friendshipService = FriendshipService.getInstance();
|
||||
storiesService = StoriesService.getInstance();
|
||||
mediaService = MediaService.getInstance();
|
||||
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
|
||||
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
|
||||
setHasOptionsMenu(true);
|
||||
@ -370,10 +373,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
if (item.getItemId() == R.id.restrict) {
|
||||
if (!isLoggedIn) return false;
|
||||
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
||||
final String action = profileModel.isRestricted() ? "Unrestrict" : "Restrict";
|
||||
friendshipService.toggleRestrict(
|
||||
profileModel.getId(),
|
||||
!profileModel.getRestricted(),
|
||||
!profileModel.isRestricted(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoRestrictRootResponse>() {
|
||||
@Override
|
||||
@ -392,7 +395,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (item.getItemId() == R.id.block) {
|
||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
if (!isLoggedIn) return false;
|
||||
if (profileModel.getBlocked()) {
|
||||
if (profileModel.isBlocked()) {
|
||||
friendshipService.unblock(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
@ -434,6 +437,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.INVISIBLE);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
|
||||
@ -571,44 +575,99 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
fetchStoryAndHighlights(profileId);
|
||||
}
|
||||
setupButtons(profileId, myId);
|
||||
if (!profileId.equals(myId)) {
|
||||
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
|
||||
favoriteRepository.getFavorite(username.substring(1), FavoriteType.USER, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
profileDetailsBinding.favCb.setChecked(true);
|
||||
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
|
||||
}
|
||||
profileDetailsBinding.favChip.setVisibility(View.VISIBLE);
|
||||
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
|
||||
favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
profileDetailsBinding.favChip.setText(R.string.added_to_favs_short);
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
result.getId(),
|
||||
profileModel.getUsername(),
|
||||
FavoriteType.USER,
|
||||
profileModel.getName(),
|
||||
profileModel.getSdProfilePic(),
|
||||
result.getDateAdded()
|
||||
), new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
profileDetailsBinding.favCb.setChecked(false);
|
||||
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
profileDetailsBinding.favCb.setVisibility(View.GONE);
|
||||
}
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
|
||||
profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
|
||||
}
|
||||
});
|
||||
profileDetailsBinding.favChip.setOnClickListener(
|
||||
v -> favoriteRepository.getFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
favoriteRepository.deleteFavorite(profileModel.getUsername(), FavoriteType.USER, new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
profileDetailsBinding.favChip.setText(R.string.add_to_favorites);
|
||||
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);
|
||||
showSnackbar(getString(R.string.removed_from_favs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
favoriteRepository.insertOrUpdateFavorite(new Favorite(
|
||||
0,
|
||||
profileModel.getUsername(),
|
||||
FavoriteType.USER,
|
||||
profileModel.getName(),
|
||||
profileModel.getSdProfilePic(),
|
||||
new Date()
|
||||
), new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
profileDetailsBinding.favChip.setText(R.string.added_to_favs);
|
||||
profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);
|
||||
showSnackbar(getString(R.string.added_to_favs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
}));
|
||||
profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getHdProfilePic());
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.VISIBLE);
|
||||
|
||||
final long followersCount = profileModel.getFollowersCount();
|
||||
final long followingCount = profileModel.getFollowingCount();
|
||||
final Long followersCount = profileModel.getFollowersCount();
|
||||
final Long followingCount = profileModel.getFollowingCount();
|
||||
|
||||
final String postCount = String.valueOf(profileModel.getPostCount());
|
||||
|
||||
SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count,
|
||||
postCount));
|
||||
SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
|
||||
profileModel.getPostCount() > 2000000000L ? 2000000000 : profileModel.getPostCount().intValue(),
|
||||
postCount));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
|
||||
profileDetailsBinding.mainPostCount.setText(span);
|
||||
profileDetailsBinding.mainPostCount.setVisibility(View.VISIBLE);
|
||||
|
||||
final String followersCountStr = String.valueOf(followersCount);
|
||||
final int followersCountStrLen = followersCountStr.length();
|
||||
span = new SpannableStringBuilder(getString(R.string.main_posts_followers,
|
||||
followersCountStr));
|
||||
span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_followers,
|
||||
followersCount > 2000000000L ? 2000000000 : followersCount.intValue(),
|
||||
followersCountStr));
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0);
|
||||
profileDetailsBinding.mainFollowers.setText(span);
|
||||
profileDetailsBinding.mainFollowers.setVisibility(View.VISIBLE);
|
||||
|
||||
final String followingCountStr = String.valueOf(followingCount);
|
||||
final int followingCountStrLen = followingCountStr.length();
|
||||
@ -617,6 +676,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0);
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
|
||||
profileDetailsBinding.mainFollowing.setText(span);
|
||||
profileDetailsBinding.mainFollowing.setVisibility(View.VISIBLE);
|
||||
|
||||
profileDetailsBinding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername()
|
||||
: profileModel.getName());
|
||||
@ -640,6 +700,51 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
.trim()));
|
||||
profileDetailsBinding.mainBiography
|
||||
.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
|
||||
profileDetailsBinding.mainBiography.setOnClickListener(v -> {
|
||||
String[] commentDialogList;
|
||||
if (!TextUtils.isEmpty(cookie)) {
|
||||
commentDialogList = new String[]{
|
||||
getResources().getString(R.string.bio_copy),
|
||||
getResources().getString(R.string.bio_translate)
|
||||
};
|
||||
} else {
|
||||
commentDialogList = new String[]{
|
||||
getResources().getString(R.string.bio_copy)
|
||||
};
|
||||
}
|
||||
new AlertDialog.Builder(context)
|
||||
.setItems(commentDialogList, (d,w) -> {
|
||||
switch (w) {
|
||||
case 0:
|
||||
Utils.copyText(context, biography);
|
||||
break;
|
||||
case 1:
|
||||
mediaService.translate(profileModel.getId(), "3", new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
if (TextUtils.isEmpty(result)) {
|
||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(profileModel.getUsername())
|
||||
.setMessage(result)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error translating bio", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
profileDetailsBinding.mainBiography.setOnLongClickListener(v -> {
|
||||
Utils.copyText(context, biography);
|
||||
return true;
|
||||
@ -687,6 +792,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
|
||||
private void setupButtons(final String profileId, final String myId) {
|
||||
profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
||||
if (isLoggedIn) {
|
||||
if (profileId.equals(myId)) {
|
||||
profileDetailsBinding.btnTagged.setVisibility(View.VISIBLE);
|
||||
@ -696,15 +802,29 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
profileDetailsBinding.btnSaved.setText(R.string.saved);
|
||||
return;
|
||||
}
|
||||
profileDetailsBinding.btnTagged.setVisibility(View.GONE);
|
||||
profileDetailsBinding.btnSaved.setVisibility(View.GONE);
|
||||
profileDetailsBinding.btnLiked.setVisibility(View.GONE);
|
||||
profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
||||
profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE);
|
||||
if (profileModel.getFollowing()) {
|
||||
if (profileModel.isFollowing() || profileModel.isFollower()) {
|
||||
profileDetailsBinding.mainStatus.setVisibility(View.VISIBLE);
|
||||
if (!profileModel.isFollowing()) {
|
||||
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.blue_800));
|
||||
profileDetailsBinding.mainStatus.setText(R.string.status_follower);
|
||||
}
|
||||
else if (!profileModel.isFollower()) {
|
||||
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.deep_orange_800));
|
||||
profileDetailsBinding.mainStatus.setText(R.string.status_following);
|
||||
}
|
||||
else {
|
||||
profileDetailsBinding.mainStatus.setChipBackgroundColor(getResources().getColorStateList(R.color.green_800));
|
||||
profileDetailsBinding.mainStatus.setText(R.string.status_mutual);
|
||||
}
|
||||
}
|
||||
if (profileModel.isFollowing()) {
|
||||
profileDetailsBinding.btnFollow.setText(R.string.unfollow);
|
||||
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else if (profileModel.getRequested()) {
|
||||
} else if (profileModel.isRequested()) {
|
||||
profileDetailsBinding.btnFollow.setText(R.string.cancel);
|
||||
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else {
|
||||
@ -713,16 +833,15 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
if (restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
if (profileModel.getRestricted()) {
|
||||
if (profileModel.isRestricted()) {
|
||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||
} else {
|
||||
restrictMenuItem.setTitle(R.string.restrict);
|
||||
}
|
||||
}
|
||||
profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
||||
if (blockMenuItem != null) {
|
||||
blockMenuItem.setVisible(true);
|
||||
if (profileModel.getBlocked()) {
|
||||
if (profileModel.isBlocked()) {
|
||||
blockMenuItem.setTitle(R.string.unblock);
|
||||
} else {
|
||||
blockMenuItem.setTitle(R.string.block);
|
||||
@ -732,7 +851,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
if (profileModel.getRestricted()) {
|
||||
if (profileModel.isRestricted()) {
|
||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||
} else {
|
||||
restrictMenuItem.setTitle(R.string.restrict);
|
||||
@ -760,20 +879,55 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
Log.e(TAG, "Error", t);
|
||||
}
|
||||
});
|
||||
new HighlightsFetcher(profileId,
|
||||
result -> {
|
||||
highlightsFetching = false;
|
||||
if (result != null) {
|
||||
profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE);
|
||||
highlightsViewModel.getList().postValue(result);
|
||||
} else profileDetailsBinding.highlightsList.setVisibility(View.GONE);
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
storiesService.fetchHighlights(profileId,
|
||||
new ServiceCallback<List<HighlightModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<HighlightModel> result) {
|
||||
highlightsFetching = false;
|
||||
if (result != null) {
|
||||
profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE);
|
||||
highlightsViewModel.getList().postValue(result);
|
||||
}
|
||||
else profileDetailsBinding.highlightsList.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
profileDetailsBinding.highlightsList.setVisibility(View.GONE);
|
||||
Log.e(TAG, "Error", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupCommonListeners() {
|
||||
final Context context = getContext();
|
||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
profileDetailsBinding.btnFollow.setOnClickListener(v -> {
|
||||
if (profileModel.getFollowing() || profileModel.getRequested()) {
|
||||
if (profileModel.isFollowing() && profileModel.isPrivate()) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.priv_acc)
|
||||
.setMessage(R.string.priv_acc_confirm)
|
||||
.setPositiveButton(R.string.confirm, (d, w) ->
|
||||
friendshipService.unfollow(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||
// Log.d(TAG, "Unfollow success: " + result);
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error unfollowing", t);
|
||||
}
|
||||
}))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
else if (profileModel.isFollowing() || profileModel.isRequested()) {
|
||||
friendshipService.unfollow(
|
||||
userIdFromCookie,
|
||||
profileModel.getId(),
|
||||
@ -854,72 +1008,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (which == 1) {
|
||||
// show stories
|
||||
final NavDirections action = ProfileFragmentDirections
|
||||
.actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username);
|
||||
.actionProfileFragmentToStoryViewerFragment(-1, null, false, false, profileModel.getId(), username, false, false);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
return;
|
||||
}
|
||||
showProfilePicDialog();
|
||||
};
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
new AlertDialog.Builder(context)
|
||||
.setItems(options, profileDialogListener)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
profileDetailsBinding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
// do not do anything if state matches the db, as listener is set before profile details are set
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final String finalUsername = username.startsWith("@") ? username.substring(1) : username;
|
||||
favoriteRepository.getFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
if (isChecked) return; // already a fav
|
||||
buttonView.setVisibility(View.GONE);
|
||||
profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
|
||||
favoriteRepository.deleteFavorite(finalUsername, FavoriteType.USER, new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24);
|
||||
profileDetailsBinding.favProgress.setVisibility(View.GONE);
|
||||
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
|
||||
showSnackbar(getString(R.string.removed_from_favs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
if (!isChecked) return; // not in fav already
|
||||
buttonView.setVisibility(View.GONE);
|
||||
profileDetailsBinding.favProgress.setVisibility(View.VISIBLE);
|
||||
final Favorite model = new Favorite(
|
||||
-1,
|
||||
finalUsername,
|
||||
FavoriteType.USER,
|
||||
profileModel.getName(),
|
||||
profileModel.getSdProfilePic(),
|
||||
new Date()
|
||||
);
|
||||
favoriteRepository.insertOrUpdateFavorite(model, new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24);
|
||||
profileDetailsBinding.favProgress.setVisibility(View.GONE);
|
||||
profileDetailsBinding.favCb.setVisibility(View.VISIBLE);
|
||||
showSnackbar(getString(R.string.added_to_favs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void showSnackbar(final String message) {
|
||||
@ -951,7 +1051,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private void setupPosts() {
|
||||
binding.postsRecyclerView.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new ProfilePostFetchService(profileModel))
|
||||
.setPostFetchService(new ProfilePostFetchService(profileModel, isLoggedIn))
|
||||
.setLayoutPreferences(layoutPreferences)
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
@ -969,7 +1069,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
highlightsViewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class);
|
||||
highlightsAdapter = new HighlightsAdapter((model, position) -> {
|
||||
final NavDirections action = ProfileFragmentDirections
|
||||
.actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null);
|
||||
.actionProfileFragmentToStoryViewerFragment(position, model.getTitle(), false, false, null, null, false, false);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
final Context context = getContext();
|
||||
|
@ -88,9 +88,10 @@ public class AboutFragment extends BasePreferencesFragment {
|
||||
preference.setSummary(R.string.about_feedback_summary);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(p -> {
|
||||
final Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse(getString(R.string.about_feedback_summary)
|
||||
+ "?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."));
|
||||
final Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("message/rfc822")
|
||||
.putExtra(Intent.EXTRA_EMAIL, getString(R.string.about_feedback_summary))
|
||||
.putExtra(Intent.EXTRA_TEXT, "Please note that your email address and the entire content will be published onto GitHub issues. If you do not wish to do that, use other contact methods instead.");
|
||||
if (intent.resolveActivity(context.getPackageManager()) != null) startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
|
@ -26,10 +26,30 @@ public class BackupPreferencesFragment extends BasePreferencesFragment {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
screen.addPreference(getAboutPreference(context));
|
||||
screen.addPreference(getWarningPreference(context));
|
||||
screen.addPreference(getCreatePreference(context));
|
||||
screen.addPreference(getRestorePreference(context));
|
||||
}
|
||||
|
||||
private Preference getAboutPreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setSummary(R.string.backup_summary);
|
||||
preference.setEnabled(false);
|
||||
preference.setIcon(R.drawable.ic_outline_info_24);
|
||||
preference.setIconSpaceReserved(true);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getWarningPreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setSummary(R.string.backup_warning);
|
||||
preference.setEnabled(false);
|
||||
preference.setIcon(R.drawable.ic_warning);
|
||||
preference.setIconSpaceReserved(true);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getCreatePreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.create_backup);
|
||||
|
@ -134,7 +134,18 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
screen.addPreference(getDivider(context));
|
||||
if (isLoggedIn) {
|
||||
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
|
||||
NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment);
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif");
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml");
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive");
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||
screen.addPreference(loggedInUsersPreferenceCategory);
|
||||
loggedInUsersPreferenceCategory.setIconSpaceReserved(false);
|
||||
loggedInUsersPreferenceCategory.setTitle(R.string.login_settings);
|
||||
loggedInUsersPreferenceCategory.addPreference(getStorySortPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getMarkStoriesSeenPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getMarkDMSeenPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getEnableActivityNotificationsPreference());
|
||||
@ -204,6 +205,25 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getStorySortPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final int length = getResources().getStringArray(R.array.story_sorts).length;
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = String.valueOf(i);
|
||||
}
|
||||
preference.setKey(Constants.STORY_SORT);
|
||||
preference.setTitle(R.string.story_sort_setting);
|
||||
preference.setDialogTitle(R.string.story_sort_setting);
|
||||
preference.setEntries(R.array.story_sorts);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setEntryValues(values);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getMarkStoriesSeenPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
|
@ -11,16 +11,12 @@ import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public abstract class BasePostModel implements Serializable, Selectable {
|
||||
protected String postId;
|
||||
protected String displayUrl;
|
||||
protected String shortCode;
|
||||
protected String postId, displayUrl, shortCode, captionId;
|
||||
protected CharSequence postCaption;
|
||||
protected MediaItemType itemType;
|
||||
protected boolean isSelected;
|
||||
protected boolean isDownloaded;
|
||||
protected boolean isSelected, isDownloaded;
|
||||
protected long timestamp;
|
||||
boolean liked;
|
||||
boolean saved;
|
||||
boolean liked, saved;
|
||||
|
||||
public boolean getLike() {
|
||||
return liked;
|
||||
@ -46,6 +42,10 @@ public abstract class BasePostModel implements Serializable, Selectable {
|
||||
return postCaption;
|
||||
}
|
||||
|
||||
public final String getCaptionId() {
|
||||
return captionId;
|
||||
}
|
||||
|
||||
public final String getShortCode() {
|
||||
return shortCode;
|
||||
}
|
||||
|
@ -11,11 +11,10 @@ public class CommentModel {
|
||||
private final ProfileModel profileModel;
|
||||
private final String id;
|
||||
private final String text;
|
||||
private final long likes;
|
||||
private long likes;
|
||||
private final long timestamp;
|
||||
private List<CommentModel> childCommentModels;
|
||||
private final boolean liked;
|
||||
private boolean hasNextPage;
|
||||
private boolean liked, hasNextPage;
|
||||
private String endCursor;
|
||||
|
||||
public CommentModel(final String id,
|
||||
@ -53,6 +52,11 @@ public class CommentModel {
|
||||
return liked;
|
||||
}
|
||||
|
||||
public void setLiked(boolean liked) {
|
||||
this.likes = liked ? likes + 1 : likes - 1;
|
||||
this.liked = liked;
|
||||
}
|
||||
|
||||
public ProfileModel getProfileModel() {
|
||||
return profileModel;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public final class FeedModel extends PostModel {
|
||||
private String displayUrl;
|
||||
private String thumbnailUrl;
|
||||
private String shortCode;
|
||||
private String postCaption;
|
||||
private String postCaption, captionId;
|
||||
private long commentsCount;
|
||||
private long timestamp;
|
||||
private boolean liked;
|
||||
@ -76,6 +76,11 @@ public final class FeedModel extends PostModel {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCaptionId(final String captionId) {
|
||||
this.captionId = captionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCommentsCount(final long commentsCount) {
|
||||
this.commentsCount = commentsCount;
|
||||
return this;
|
||||
@ -127,8 +132,8 @@ public final class FeedModel extends PostModel {
|
||||
}
|
||||
|
||||
public FeedModel build() {
|
||||
return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, commentsCount,
|
||||
timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
|
||||
return new FeedModel(profileModel, itemType, viewCount, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId,
|
||||
commentsCount, timestamp, liked, bookmarked, likesCount, locationName, locationId, sliderItems, imageHeight, imageWidth);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +145,7 @@ public final class FeedModel extends PostModel {
|
||||
final String thumbnailUrl,
|
||||
final String shortCode,
|
||||
final String postCaption,
|
||||
final String captionId,
|
||||
final long commentsCount,
|
||||
final long timestamp,
|
||||
final boolean liked,
|
||||
@ -150,7 +156,7 @@ public final class FeedModel extends PostModel {
|
||||
final List<PostChild> sliderItems,
|
||||
final int imageHeight,
|
||||
final int imageWidth) {
|
||||
super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, timestamp, liked, bookmarked);
|
||||
super(itemType, postId, displayUrl, thumbnailUrl, shortCode, postCaption, captionId, timestamp, liked, bookmarked);
|
||||
this.profileModel = profileModel;
|
||||
this.commentsCount = commentsCount;
|
||||
this.likesCount = likesCount;
|
||||
|
@ -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 getFirstStoryModel() {
|
||||
return firstStoryModel;
|
||||
}
|
||||
|
||||
public StoryModel[] getStoryModels() {
|
||||
return storyModels;
|
||||
}
|
||||
|
||||
public boolean getFullyRead() {
|
||||
public Boolean isFullyRead() {
|
||||
return fullyRead;
|
||||
}
|
||||
|
||||
public void setFullyRead(final boolean fullyRead) {
|
||||
this.fullyRead = fullyRead;
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ public final class HashtagModel implements Serializable {
|
||||
return sdProfilePic;
|
||||
}
|
||||
|
||||
public long getPostCount() { return postCount; }
|
||||
public Long getPostCount() { return postCount; }
|
||||
|
||||
public boolean getFollowing() { return following; }
|
||||
}
|
@ -1,16 +1,28 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public final class HighlightModel {
|
||||
private final String title;
|
||||
private final String id;
|
||||
private final String thumbnailUrl;
|
||||
private final long timestamp;
|
||||
private final int mediaCount;
|
||||
|
||||
public HighlightModel(final String title,
|
||||
final String id,
|
||||
final String thumbnailUrl) {
|
||||
final String thumbnailUrl,
|
||||
final long timestamp,
|
||||
final int mediaCount) {
|
||||
this.title = title;
|
||||
this.id = id;
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.timestamp = timestamp;
|
||||
this.mediaCount = mediaCount;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
@ -24,4 +36,17 @@ public final class HighlightModel {
|
||||
public String getThumbnailUrl() {
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
|
||||
}
|
||||
|
||||
public int getMediaCount() {
|
||||
return mediaCount;
|
||||
}
|
||||
}
|
@ -59,5 +59,5 @@ public final class LocationModel implements Serializable {
|
||||
return sdProfilePic;
|
||||
}
|
||||
|
||||
public long getPostCount() { return postCount; }
|
||||
public Long getPostCount() { return postCount; }
|
||||
}
|
@ -13,7 +13,7 @@ public final class NotificationModel {
|
||||
private final String userId;
|
||||
private final String username;
|
||||
private final String profilePicUrl;
|
||||
private final String shortCode;
|
||||
private final String postId;
|
||||
private final String previewUrl;
|
||||
private final NotificationType type;
|
||||
private final CharSequence text;
|
||||
@ -25,7 +25,7 @@ public final class NotificationModel {
|
||||
final String userId,
|
||||
final String username,
|
||||
final String profilePicUrl,
|
||||
final String shortCode,
|
||||
final String postId,
|
||||
final String previewUrl,
|
||||
final NotificationType type) {
|
||||
this.id = id;
|
||||
@ -34,7 +34,7 @@ public final class NotificationModel {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.profilePicUrl = profilePicUrl;
|
||||
this.shortCode = shortCode;
|
||||
this.postId = postId;
|
||||
this.previewUrl = previewUrl;
|
||||
this.type = type;
|
||||
}
|
||||
@ -47,6 +47,10 @@ public final class NotificationModel {
|
||||
return text;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
|
||||
@ -64,8 +68,8 @@ public final class NotificationModel {
|
||||
return profilePicUrl;
|
||||
}
|
||||
|
||||
public String getShortCode() {
|
||||
return shortCode;
|
||||
public String getPostId() {
|
||||
return postId;
|
||||
}
|
||||
|
||||
public String getPreviewPic() {
|
||||
|
@ -19,6 +19,7 @@ public class PostModel extends BasePostModel {
|
||||
final String thumbnailUrl,
|
||||
final String shortCode,
|
||||
final CharSequence postCaption,
|
||||
final String captionId,
|
||||
long timestamp,
|
||||
boolean liked,
|
||||
boolean bookmarked) {
|
||||
@ -28,6 +29,7 @@ public class PostModel extends BasePostModel {
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.shortCode = shortCode;
|
||||
this.postCaption = postCaption;
|
||||
this.captionId = captionId;
|
||||
this.timestamp = timestamp;
|
||||
this.liked = liked;
|
||||
this.saved = bookmarked;
|
||||
|
@ -3,15 +3,15 @@ package awais.instagrabber.models;
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class ProfileModel implements Serializable {
|
||||
private final boolean isPrivate, reallyPrivate, isVerified, following, restricted, blocked, requested;
|
||||
private final boolean isPrivate, reallyPrivate, isVerified, following, follower, restricted, blocked, requested;
|
||||
private final long postCount, followersCount, followingCount;
|
||||
private final String id, username, name, biography, url, sdProfilePic, hdProfilePic;
|
||||
|
||||
public ProfileModel(final boolean isPrivate, final boolean reallyPrivate,
|
||||
final boolean isVerified, final String id, final String username, final String name, final String biography,
|
||||
final String url, final String sdProfilePic, final String hdProfilePic, final long postCount,
|
||||
final long followersCount, final long followingCount, final boolean following, final boolean restricted,
|
||||
final boolean blocked, final boolean requested) {
|
||||
final long followersCount, final long followingCount, final boolean following, final boolean follower,
|
||||
final boolean restricted, final boolean blocked, final boolean requested) {
|
||||
this.isPrivate = isPrivate;
|
||||
this.reallyPrivate = reallyPrivate;
|
||||
this.isVerified = isVerified;
|
||||
@ -26,21 +26,22 @@ public final class ProfileModel implements Serializable {
|
||||
this.followersCount = followersCount;
|
||||
this.followingCount = followingCount;
|
||||
this.following = following;
|
||||
this.follower = follower;
|
||||
this.restricted = restricted;
|
||||
this.blocked = blocked;
|
||||
this.requested = requested;
|
||||
}
|
||||
|
||||
public static ProfileModel getDefaultProfileModel() {
|
||||
return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
|
||||
return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
|
||||
}
|
||||
|
||||
public static ProfileModel getDefaultProfileModel(final String userId) {
|
||||
return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
|
||||
return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
|
||||
}
|
||||
|
||||
public static ProfileModel getDefaultProfileModel(final String userId, final String username) {
|
||||
return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false);
|
||||
return new ProfileModel(false, false, false, userId, username, null, null, null, null, null, 0, 0, 0, false, false, false, false, false);
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
@ -83,31 +84,35 @@ public final class ProfileModel implements Serializable {
|
||||
return hdProfilePic;
|
||||
}
|
||||
|
||||
public long getPostCount() {
|
||||
public Long getPostCount() {
|
||||
return postCount;
|
||||
}
|
||||
|
||||
public long getFollowersCount() {
|
||||
public Long getFollowersCount() {
|
||||
return followersCount;
|
||||
}
|
||||
|
||||
public long getFollowingCount() {
|
||||
public Long getFollowingCount() {
|
||||
return followingCount;
|
||||
}
|
||||
|
||||
public boolean getFollowing() {
|
||||
public boolean isFollowing() {
|
||||
return following;
|
||||
}
|
||||
|
||||
public boolean getRestricted() {
|
||||
public boolean isFollower() {
|
||||
return follower;
|
||||
}
|
||||
|
||||
public boolean isRestricted() {
|
||||
return restricted;
|
||||
}
|
||||
|
||||
public boolean getBlocked() {
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public boolean getRequested() {
|
||||
public boolean isRequested() {
|
||||
return requested;
|
||||
}
|
||||
}
|
@ -6,11 +6,13 @@ import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.models.stickers.PollModel;
|
||||
import awais.instagrabber.models.stickers.QuestionModel;
|
||||
import awais.instagrabber.models.stickers.QuizModel;
|
||||
import awais.instagrabber.models.stickers.SliderModel;
|
||||
import awais.instagrabber.models.stickers.SwipeUpModel;
|
||||
|
||||
public final class StoryModel implements Serializable {
|
||||
private final String storyMediaId;
|
||||
private final String storyUrl;
|
||||
private String thumbnail;
|
||||
private final String username;
|
||||
private final String userId;
|
||||
private final MediaItemType itemType;
|
||||
@ -21,6 +23,7 @@ public final class StoryModel implements Serializable {
|
||||
private String spotify;
|
||||
private PollModel poll;
|
||||
private QuestionModel question;
|
||||
private SliderModel slider;
|
||||
private QuizModel quiz;
|
||||
private SwipeUpModel swipeUp;
|
||||
private String[] mentions;
|
||||
@ -28,10 +31,11 @@ public final class StoryModel implements Serializable {
|
||||
private boolean isCurrentSlide = false;
|
||||
private final boolean canReply;
|
||||
|
||||
public StoryModel(final String storyMediaId, final String storyUrl, final MediaItemType itemType,
|
||||
public StoryModel(final String storyMediaId, final String storyUrl, final String thumbnail, final MediaItemType itemType,
|
||||
final long timestamp, final String username, final String userId, final boolean canReply) {
|
||||
this.storyMediaId = storyMediaId;
|
||||
this.storyUrl = storyUrl;
|
||||
this.thumbnail = thumbnail;
|
||||
this.itemType = itemType;
|
||||
this.timestamp = timestamp;
|
||||
this.username = username;
|
||||
@ -43,6 +47,10 @@ public final class StoryModel implements Serializable {
|
||||
return storyUrl;
|
||||
}
|
||||
|
||||
public String getThumbnail() {
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
public String getStoryMediaId() {
|
||||
return storyMediaId;
|
||||
}
|
||||
@ -71,6 +79,10 @@ public final class StoryModel implements Serializable {
|
||||
return question;
|
||||
}
|
||||
|
||||
public SliderModel getSlider() {
|
||||
return slider;
|
||||
}
|
||||
|
||||
public QuizModel getQuiz() {
|
||||
return quiz;
|
||||
}
|
||||
@ -85,6 +97,10 @@ public final class StoryModel implements Serializable {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setThumbnail(final String thumbnail) {
|
||||
this.thumbnail = thumbnail;
|
||||
}
|
||||
|
||||
public void setVideoUrl(final String videoUrl) {
|
||||
this.videoUrl = videoUrl;
|
||||
}
|
||||
@ -109,6 +125,10 @@ public final class StoryModel implements Serializable {
|
||||
this.question = question;
|
||||
}
|
||||
|
||||
public void setSlider(final SliderModel slider) {
|
||||
this.slider = slider;
|
||||
}
|
||||
|
||||
public void setQuiz(final QuizModel quiz) {
|
||||
this.quiz = quiz;
|
||||
}
|
||||
|
@ -5,12 +5,20 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum NotificationType implements Serializable {
|
||||
// web
|
||||
LIKE("GraphLikeAggregatedStory"),
|
||||
FOLLOW("GraphFollowAggregatedStory"),
|
||||
COMMENT("GraphCommentMediaStory"),
|
||||
MENTION("GraphMentionStory"),
|
||||
TAGGED("GraphUserTaggedStory"),
|
||||
REQUEST("REQUEST");
|
||||
// app story_type
|
||||
COMMENT_LIKE("13"),
|
||||
TAGGED_COMMENT("14"),
|
||||
RESPONDED_STORY("213"),
|
||||
// efr - random value
|
||||
REQUEST("REQUEST"),
|
||||
// ayml - random value
|
||||
AYML("AYML");
|
||||
|
||||
private final String itemType;
|
||||
private static final Map<String, NotificationType> map = new HashMap<>();
|
||||
|
53
app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java
Executable file
53
app/src/main/java/awais/instagrabber/models/stickers/SliderModel.java
Executable file
@ -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;
|
||||
}
|
||||
}
|
@ -3,10 +3,12 @@ package awais.instagrabber.repositories;
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.QueryMap;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
public interface FeedRepository {
|
||||
@GET("/graphql/query/")
|
||||
Call<String> fetch(@QueryMap(encoded = true) Map<String, String> queryParams);
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/feed/timeline/")
|
||||
Call<String> fetch(@FieldMap final Map<String, String> signedForm);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -12,7 +12,4 @@ public interface LocationRepository {
|
||||
@GET("/api/v1/feed/location/{location}/")
|
||||
Call<String> fetchPosts(@Path("location") final String locationId,
|
||||
@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/graphql/query/")
|
||||
Call<String> fetchGraphQLPosts(@QueryMap(encoded = true) Map<String, String> queryParams);
|
||||
}
|
||||
|
@ -5,48 +5,57 @@ import java.util.Map;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
public interface MediaRepository {
|
||||
@GET("/api/v1/media/{mediaId}/info/")
|
||||
Call<String> fetch(@Path("mediaId") final String mediaId);
|
||||
|
||||
@GET("/api/v1/media/{mediaId}/{action}/")
|
||||
Call<String> fetchLikes(@Path("mediaId") final String mediaId,
|
||||
@Path("action") final String action); // one of "likers" or "comment_likers"
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{mediaId}/{action}/")
|
||||
Call<String> action(@Header("User-Agent") final String userAgent,
|
||||
@Path("action") final String action,
|
||||
Call<String> action(@Path("action") final String action,
|
||||
@Path("mediaId") final String mediaId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{mediaId}/comment/")
|
||||
Call<String> comment(@Header("User-Agent") final String userAgent,
|
||||
@Path("mediaId") final String mediaId,
|
||||
Call<String> comment(@Path("mediaId") final String mediaId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{mediaId}/comment/bulk_delete/")
|
||||
Call<String> commentsBulkDelete(@Header("User-Agent") final String userAgent,
|
||||
@Path("mediaId") final String mediaId,
|
||||
Call<String> commentsBulkDelete(@Path("mediaId") final String mediaId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{commentId}/comment_like/")
|
||||
Call<String> commentLike(@Header("User-Agent") final String userAgent,
|
||||
@Path("commentId") final String commentId,
|
||||
Call<String> commentLike(@Path("commentId") final String commentId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{commentId}/comment_unlike/")
|
||||
Call<String> commentUnlike(@Header("User-Agent") final String userAgent,
|
||||
@Path("commentId") final String commentId,
|
||||
Call<String> commentUnlike(@Path("commentId") final String commentId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{mediaId}/edit_media/")
|
||||
Call<String> editCaption(@Path("mediaId") final String mediaId,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
|
||||
@GET("/api/v1/language/translate/")
|
||||
Call<String> translate(@QueryMap final Map<String, String> form);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/upload_finish/")
|
||||
Call<String> uploadFinish(// @Header("User-Agent") final String userAgent,
|
||||
@Header("retry_context") final String retryContext,
|
||||
Call<String> uploadFinish(@Header("retry_context") final String retryContext,
|
||||
@QueryMap Map<String, String> queryParams,
|
||||
@FieldMap final Map<String, String> signedForm);
|
||||
}
|
||||
|
@ -6,16 +6,24 @@ import awais.instagrabber.utils.Constants;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface NewsRepository {
|
||||
|
||||
Call<String> inbox();
|
||||
@Headers("User-Agent: " + Constants.USER_AGENT)
|
||||
@GET("https://www.instagram.com/accounts/activity/?__a=1")
|
||||
Call<String> webInbox();
|
||||
|
||||
@Headers("User-Agent: " + Constants.I_USER_AGENT)
|
||||
@GET("/api/v1/news/inbox/")
|
||||
Call<String> appInbox(@Query(value = "mark_as_seen", encoded = true) boolean markAsSeen);
|
||||
|
||||
@FormUrlEncoded
|
||||
@Headers("User-Agent: " + Constants.USER_AGENT)
|
||||
@POST("https://www.instagram.com/web/activity/mark_checked/")
|
||||
Call<String> markChecked(@Header("x-csrftoken") String csrfToken, @FieldMap Map<String, String> map);
|
||||
@Headers("User-Agent: " + Constants.I_USER_AGENT)
|
||||
@POST("/api/v1/discover/ayml/")
|
||||
Call<String> getAyml(@FieldMap final Map<String, String> form);
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ import retrofit2.http.QueryMap;
|
||||
|
||||
public interface ProfileRepository {
|
||||
|
||||
@GET("api/v1/users/{uid}/info/")
|
||||
@GET("/api/v1/users/{uid}/info/")
|
||||
Call<String> getUserInfo(@Path("uid") final String uid);
|
||||
|
||||
@GET("/graphql/query/")
|
||||
Call<String> fetch(@QueryMap Map<String, String> queryMap);
|
||||
@GET("/api/v1/feed/user/{uid}/")
|
||||
Call<String> fetch(@Path("uid") final String uid, @QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/api/v1/feed/saved/")
|
||||
Call<String> fetchSaved(@QueryMap Map<String, String> queryParams);
|
||||
|
@ -2,17 +2,40 @@ package awais.instagrabber.repositories;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.repositories.responses.StoryStickerResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.QueryMap;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
public interface StoriesRepository {
|
||||
@GET("/api/v1/media/{mediaId}/info/")
|
||||
Call<String> fetch(@Path("mediaId") final String mediaId);
|
||||
// this one is the same as MediaRepository.fetch BUT you need to make sure it's a story
|
||||
|
||||
@GET("graphql/query/")
|
||||
Call<String> getStories(@QueryMap(encoded = true) Map<String, String> variables);
|
||||
@GET("/api/v1/feed/reels_tray/")
|
||||
Call<String> getFeedStories();
|
||||
|
||||
@GET("/api/v1/highlights/{uid}/highlights_tray/")
|
||||
Call<String> fetchHighlights(@Path("uid") final String uid);
|
||||
|
||||
@GET("/api/v1/archive/reel/day_shells/")
|
||||
Call<String> fetchArchive(@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET
|
||||
Call<String> getUserStory(@Header("User-Agent") String userAgent, @Url String url);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/media/{storyId}/{stickerId}/{action}/")
|
||||
Call<StoryStickerResponse> respondToSticker(@Header("User-Agent") String userAgent,
|
||||
@Path("storyId") String storyId,
|
||||
@Path("stickerId") String stickerId,
|
||||
@Path("action") String action,
|
||||
// story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer
|
||||
@FieldMap Map<String, String> form);
|
||||
}
|
||||
|
@ -24,7 +24,4 @@ public interface TagsRepository {
|
||||
@GET("/api/v1/feed/tag/{tag}/")
|
||||
Call<String> fetchPosts(@Path("tag") final String tag,
|
||||
@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/graphql/query/")
|
||||
Call<String> fetchGraphQLPosts(@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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -2,15 +2,18 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class UserInfo {
|
||||
private final String pk;
|
||||
private final String username;
|
||||
private final String fullName;
|
||||
private final String profilePicUrl;
|
||||
private final String username, fullName, profilePicUrl, hdProfilePicUrl;
|
||||
|
||||
public UserInfo(final String pk, final String username, final String fullName, final String profilePicUrl) {
|
||||
public UserInfo(final String pk,
|
||||
final String username,
|
||||
final String fullName,
|
||||
final String profilePicUrl,
|
||||
final String hdProfilePicUrl) {
|
||||
this.pk = pk;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.profilePicUrl = profilePicUrl;
|
||||
this.hdProfilePicUrl = hdProfilePicUrl;
|
||||
}
|
||||
|
||||
public String getPk() {
|
||||
@ -29,6 +32,10 @@ public class UserInfo {
|
||||
return profilePicUrl;
|
||||
}
|
||||
|
||||
public String getHDProfilePicUrl() {
|
||||
return hdProfilePicUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserInfo{" +
|
||||
@ -36,6 +43,7 @@ public class UserInfo {
|
||||
", username='" + username + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", profilePicUrl='" + profilePicUrl + '\'' +
|
||||
", hdProfilePicUrl='" + hdProfilePicUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -99,11 +99,11 @@ public class DirectUser {
|
||||
profileModel.getSdProfilePic(),
|
||||
null,
|
||||
new DirectUserFriendshipStatus(
|
||||
profileModel.getFollowing(),
|
||||
profileModel.getBlocked(),
|
||||
profileModel.isFollowing(),
|
||||
profileModel.isBlocked(),
|
||||
profileModel.isPrivate(),
|
||||
false,
|
||||
profileModel.getRequested(),
|
||||
profileModel.isRequested(),
|
||||
false
|
||||
),
|
||||
profileModel.isVerified(),
|
||||
|
@ -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:Barinsta@AustinHuang.me";
|
||||
// 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\":" +
|
||||
|
@ -152,7 +152,7 @@ public final class ExportImportUtils {
|
||||
continue;
|
||||
}
|
||||
final Favorite favorite = new Favorite(
|
||||
-1,
|
||||
0,
|
||||
query,
|
||||
favoriteType,
|
||||
favsObject.optString("s"),
|
||||
|
@ -73,6 +73,10 @@ public final class LocaleUtils {
|
||||
if (appLanguageIndex == 14) return "zh_TW";
|
||||
if (appLanguageIndex == 15) return "ca";
|
||||
if (appLanguageIndex == 16) return "ru";
|
||||
if (appLanguageIndex == 17) return "hi";
|
||||
if (appLanguageIndex == 18) return "nl";
|
||||
if (appLanguageIndex == 19) return "sk";
|
||||
if (appLanguageIndex == 20) return "ja";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -20,11 +20,17 @@ import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostChild;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
||||
import awais.instagrabber.repositories.responses.directmessages.ImageVersions2;
|
||||
import awais.instagrabber.repositories.responses.directmessages.MediaCandidate;
|
||||
import awais.instagrabber.models.stickers.PollModel;
|
||||
import awais.instagrabber.models.stickers.QuestionModel;
|
||||
import awais.instagrabber.models.stickers.QuizModel;
|
||||
import awais.instagrabber.models.stickers.SliderModel;
|
||||
import awais.instagrabber.models.stickers.SwipeUpModel;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
public final class ResponseBodyUtils {
|
||||
@ -186,8 +192,8 @@ public final class ResponseBodyUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
// public static DirectItemMediaModel getDirectMediaModel(final JSONObject mediaObj) throws Exception {
|
||||
// final DirectItemMediaModel mediaModel;
|
||||
// public static DirectItemModel.DirectItemMediaModel getDirectMediaModel(final JSONObject mediaObj) throws Exception {
|
||||
// final DirectItemModel.DirectItemMediaModel mediaModel;
|
||||
// if (mediaObj == null) mediaModel = null;
|
||||
// else {
|
||||
// final JSONObject userObj = mediaObj.optJSONObject("user");
|
||||
@ -203,7 +209,7 @@ public final class ResponseBodyUtils {
|
||||
// userObj.getString("full_name"),
|
||||
// null, null,
|
||||
// userObj.getString("profile_pic_url"),
|
||||
// null, 0, 0, 0, false, false, false, false);
|
||||
// null, 0, 0, 0, false, false, false, false, false);
|
||||
// }
|
||||
//
|
||||
// final MediaItemType mediaType = getMediaItemType(mediaObj.optInt("media_type", -1));
|
||||
@ -212,7 +218,7 @@ public final class ResponseBodyUtils {
|
||||
// if (TextUtils.isEmpty(id)) id = null;
|
||||
//
|
||||
// final ThumbnailDetails thumbnailDetails = getThumbnailUrl(mediaObj, mediaType);
|
||||
// mediaModel = new DirectItemMediaModel(
|
||||
// mediaModel = new DirectItemModel.DirectItemMediaModel(
|
||||
// mediaType,
|
||||
// mediaObj.optLong("expiring_at"),
|
||||
// mediaObj.optLong("pk"),
|
||||
@ -226,7 +232,7 @@ public final class ResponseBodyUtils {
|
||||
// }
|
||||
// return mediaModel;
|
||||
// }
|
||||
|
||||
//
|
||||
// private static DirectItemType getDirectItemType(final String itemType) {
|
||||
// if ("placeholder".equals(itemType)) return DirectItemType.PLACEHOLDER;
|
||||
// if ("media".equals(itemType)) return DirectItemType.MEDIA;
|
||||
@ -245,81 +251,99 @@ public final class ResponseBodyUtils {
|
||||
// if ("felix_share".equals(itemType)) return DirectItemType.FELIX_SHARE;
|
||||
// return DirectItemType.TEXT;
|
||||
// }
|
||||
|
||||
//
|
||||
// @NonNull
|
||||
// public static InboxThreadModel createInboxThreadModel(@NonNull final JSONObject data, final boolean inThreadView) throws Exception {
|
||||
// final InboxReadState readState = data.getInt("read_state") == 0 ? InboxReadState.STATE_READ : InboxReadState.STATE_UNREAD;
|
||||
// final String threadType = data.getString("thread_type"); // they're all "private", group is identified by boolean "is_group"
|
||||
//
|
||||
// final JSONArray leftUsersJsonArray = data.optJSONArray("left_users");
|
||||
// final String threadId = data.getString("thread_id");
|
||||
// final String threadV2Id = data.getString("thread_v2_id");
|
||||
// final String threadTitle = data.getString("thread_title");
|
||||
//
|
||||
// final JSONArray usersJsonArray = data.getJSONArray("users");
|
||||
// final List<ProfileModel> users = new ArrayList<>();
|
||||
// for (int j = 0; j < usersJsonArray.length(); ++j) {
|
||||
// final JSONObject userObject = usersJsonArray.getJSONObject(j);
|
||||
// users.add(new ProfileModel(userObject.optBoolean("is_private"),
|
||||
// false,
|
||||
// userObject.optBoolean("is_verified"),
|
||||
// String.valueOf(userObject.get("pk")),
|
||||
// userObject.optString("username"), // optional cuz fb users
|
||||
// userObject.getString("full_name"),
|
||||
// null,
|
||||
// null,
|
||||
// userObject.getString("profile_pic_url"),
|
||||
// null,
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false));
|
||||
// }
|
||||
// final String threadNewestCursor = data.optString("newest_cursor");
|
||||
// final String threadOldestCursor = data.optString("oldest_cursor");
|
||||
// final String threadNextCursor = data.has("next_cursor") ? data.getString("next_cursor") : null;
|
||||
// final String threadPrevCursor = data.has("prev_cursor") ? data.getString("prev_cursor") : null;
|
||||
//
|
||||
// final List<ProfileModel> leftUsers = new ArrayList<>();
|
||||
// for (int j = 0; j < leftUsersJsonArray.length(); ++j) {
|
||||
// final JSONObject userObject = leftUsersJsonArray.getJSONObject(j);
|
||||
// leftUsers.add(new ProfileModel(userObject.getBoolean("is_private"),
|
||||
// false,
|
||||
// userObject.optBoolean("is_verified"),
|
||||
// String.valueOf(userObject.get("pk")),
|
||||
// userObject.getString("username"),
|
||||
// userObject.getString("full_name"),
|
||||
// null,
|
||||
// null,
|
||||
// userObject.getString("profile_pic_url"),
|
||||
// null,
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false));
|
||||
// }
|
||||
// final boolean threadHasOlder = data.getBoolean("has_older");
|
||||
// final long unreadCount = data.optLong("read_state", 0);
|
||||
//
|
||||
// final long lastActivityAt = data.optLong("last_activity_at");
|
||||
// final boolean named = data.optBoolean("named");
|
||||
// final boolean muted = data.optBoolean("muted");
|
||||
// final boolean isPin = data.optBoolean("is_pin");
|
||||
// final boolean isSpam = data.optBoolean("is_spam");
|
||||
// final boolean isGroup = data.optBoolean("is_group");
|
||||
// final boolean pending = data.optBoolean("pending");
|
||||
// final boolean archived = data.optBoolean("archived");
|
||||
// final boolean canonical = data.optBoolean("canonical");
|
||||
//
|
||||
// final JSONArray users = data.getJSONArray("users");
|
||||
// final int usersLen = users.length();
|
||||
// final JSONArray leftusers = data.optJSONArray("left_users");
|
||||
// final int leftusersLen = leftusers == null ? 0 : leftusers.length();
|
||||
// final JSONArray admins = data.getJSONArray("admin_user_ids");
|
||||
// final List<Long> adminIDs = new ArrayList<>();
|
||||
// for (int j = 0; j < admins.length(); ++j) {
|
||||
// adminIDs.add(admins.getLong(j));
|
||||
// final int adminsLen = admins.length();
|
||||
//
|
||||
// final ProfileModel[] userModels = new ProfileModel[usersLen];
|
||||
// for (int j = 0; j < usersLen; ++j) {
|
||||
// final JSONObject userObject = users.getJSONObject(j);
|
||||
// userModels[j] = new ProfileModel(userObject.optBoolean("is_private"),
|
||||
// false,
|
||||
// userObject.optBoolean("is_verified"),
|
||||
// String.valueOf(userObject.get("pk")),
|
||||
// userObject.optString("username"), // optional cuz fb users
|
||||
// userObject.getString("full_name"),
|
||||
// null, null,
|
||||
// userObject.getString("profile_pic_url"),
|
||||
// null, 0, 0, 0, false, false, false, false, false);
|
||||
// }
|
||||
//
|
||||
// final ProfileModel[] leftuserModels = new ProfileModel[leftusersLen];
|
||||
// for (int j = 0; j < leftusersLen; ++j) {
|
||||
// final JSONObject userObject = leftusers.getJSONObject(j);
|
||||
// leftuserModels[j] = new ProfileModel(userObject.getBoolean("is_private"),
|
||||
// false,
|
||||
// userObject.optBoolean("is_verified"),
|
||||
// String.valueOf(userObject.get("pk")),
|
||||
// userObject.getString("username"),
|
||||
// userObject.getString("full_name"),
|
||||
// null, null,
|
||||
// userObject.getString("profile_pic_url"),
|
||||
// null, 0, 0, 0, false, false, false, false, false);
|
||||
// }
|
||||
//
|
||||
// final Long[] adminIDs = new Long[adminsLen];
|
||||
// for (int j = 0; j < adminsLen; ++j) {
|
||||
// adminIDs[j] = admins.getLong(j);
|
||||
// }
|
||||
//
|
||||
// final JSONArray items = data.getJSONArray("items");
|
||||
// final int itemsLen = items.length();
|
||||
//
|
||||
// final List<DirectItemModel> itemModels = new ArrayList<>(itemsLen);
|
||||
// final ArrayList<DirectItemModel> itemModels = new ArrayList<>(itemsLen);
|
||||
// for (int i = 0; i < itemsLen; ++i) {
|
||||
// final JSONObject itemObject = items.getJSONObject(i);
|
||||
//
|
||||
// CharSequence text = null;
|
||||
// ProfileModel profileModel = null;
|
||||
// DMItemMediaModel mediaModel = null;
|
||||
// DirectItemModel.DirectItemLinkModel linkModel = null;
|
||||
// DirectItemModel.DirectItemMediaModel directMedia = null;
|
||||
// DirectItemModel.DirectItemReelShareModel reelShareModel = null;
|
||||
// DirectItemModel.DirectItemActionLogModel actionLogModel = null;
|
||||
// DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = null;
|
||||
// DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = null;
|
||||
// DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = null;
|
||||
// DirectItemModel.DirectItemVideoCallEventModel videoCallEventModel = null;
|
||||
//
|
||||
// final DirectItemType itemType = getDirectItemType(itemObject.getString("item_type"));
|
||||
// switch (itemType) {
|
||||
// case ANIMATED_MEDIA: {
|
||||
// final JSONObject animatedMedia = itemObject.getJSONObject("animated_media");
|
||||
// final JSONObject stickerImage = animatedMedia.getJSONObject("images").getJSONObject("fixed_height");
|
||||
// mediaModel = new DirectItemAnimatedMediaModel(
|
||||
//
|
||||
// animatedMediaModel = new DirectItemModel.DirectItemAnimatedMediaModel(
|
||||
// animatedMedia.getBoolean("is_random"),
|
||||
// animatedMedia.getBoolean("is_sticker"),
|
||||
// animatedMedia.getString("id"),
|
||||
@ -330,9 +354,11 @@ public final class ResponseBodyUtils {
|
||||
// stickerImage.getInt("width"));
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case VOICE_MEDIA: {
|
||||
// final JSONObject voiceMedia = itemObject.getJSONObject("voice_media").getJSONObject("media");
|
||||
// final JSONObject audio = voiceMedia.getJSONObject("audio");
|
||||
//
|
||||
// int[] waveformData = null;
|
||||
// final JSONArray waveformDataArray = audio.optJSONArray("waveform_data");
|
||||
// if (waveformDataArray != null) {
|
||||
@ -343,35 +369,40 @@ public final class ResponseBodyUtils {
|
||||
// waveformData[j] = (int) (waveformDataArray.optDouble(j) * 10);
|
||||
// }
|
||||
// }
|
||||
// mediaModel = new DirectItemVoiceMediaModel(
|
||||
//
|
||||
// voiceMediaModel = new DirectItemModel.DirectItemVoiceMediaModel(
|
||||
// voiceMedia.getString("id"),
|
||||
// audio.getString("audio_src"),
|
||||
// audio.getLong("duration"),
|
||||
// waveformData);
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case LINK: {
|
||||
// final JSONObject linkObj = itemObject.getJSONObject("link");
|
||||
// DirectItemLinkContext itemLinkContext = null;
|
||||
//
|
||||
// DirectItemModel.DirectItemLinkContext itemLinkContext = null;
|
||||
// final JSONObject linkContext = linkObj.optJSONObject("link_context");
|
||||
// if (linkContext != null) {
|
||||
// itemLinkContext = new DirectItemLinkContext(
|
||||
// itemLinkContext = new DirectItemModel.DirectItemLinkContext(
|
||||
// linkContext.getString("link_url"),
|
||||
// linkContext.optString("link_title"),
|
||||
// linkContext.optString("link_summary"),
|
||||
// linkContext.optString("link_image_url")
|
||||
// );
|
||||
// }
|
||||
// mediaModel = new DirectItemLinkModel(
|
||||
//
|
||||
// linkModel = new DirectItemModel.DirectItemLinkModel(
|
||||
// linkObj.getString("text"),
|
||||
// linkObj.getString("client_context"),
|
||||
// linkObj.optString("mutation_token"),
|
||||
// itemLinkContext);
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case REEL_SHARE: {
|
||||
// final JSONObject reelShare = itemObject.getJSONObject("reel_share");
|
||||
// mediaModel = new DirectItemReelShareModel(
|
||||
// reelShareModel = new DirectItemModel.DirectItemReelShareModel(
|
||||
// reelShare.optBoolean("is_reel_persisted"),
|
||||
// reelShare.getLong("reel_owner_id"),
|
||||
// reelShare.getJSONObject("media").getJSONObject("user").getString("username"),
|
||||
@ -383,34 +414,39 @@ public final class ResponseBodyUtils {
|
||||
// getDirectMediaModel(reelShare.optJSONObject("media")));
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case RAVEN_MEDIA: {
|
||||
// final JSONObject visualMedia = itemObject.getJSONObject("visual_media");
|
||||
//
|
||||
// final JSONArray seenUserIdsArray = visualMedia.getJSONArray("seen_user_ids");
|
||||
// final int seenUsersLen = seenUserIdsArray.length();
|
||||
// final String[] seenUserIds = new String[seenUsersLen];
|
||||
// for (int j = 0; j < seenUsersLen; j++)
|
||||
// seenUserIds[j] = seenUserIdsArray.getString(j);
|
||||
// RavenExpiringMediaActionSummary expiringSummaryModel = null;
|
||||
//
|
||||
// DirectItemModel.RavenExpiringMediaActionSummaryModel expiringSummaryModel = null;
|
||||
// final JSONObject actionSummary = visualMedia.optJSONObject("expiring_media_action_summary");
|
||||
// if (actionSummary != null)
|
||||
// expiringSummaryModel = new RavenExpiringMediaActionSummary(
|
||||
// expiringSummaryModel = new DirectItemModel.RavenExpiringMediaActionSummaryModel(
|
||||
// actionSummary.getLong("timestamp"), actionSummary.getInt("count"),
|
||||
// getExpiringMediaType(actionSummary.getString("type")));
|
||||
// final RavenMediaViewMode viewType;
|
||||
//
|
||||
// final RavenMediaViewType viewType;
|
||||
// final String viewMode = visualMedia.getString("view_mode");
|
||||
// switch (viewMode) {
|
||||
// case "replayable":
|
||||
// viewType = RavenMediaViewMode.REPLAYABLE;
|
||||
// viewType = RavenMediaViewType.REPLAYABLE;
|
||||
// break;
|
||||
// case "permanent":
|
||||
// viewType = RavenMediaViewMode.PERMANENT;
|
||||
// viewType = RavenMediaViewType.PERMANENT;
|
||||
// break;
|
||||
// case "once":
|
||||
// default:
|
||||
// viewType = RavenMediaViewMode.ONCE;
|
||||
// viewType = RavenMediaViewType.ONCE;
|
||||
// }
|
||||
// mediaModel = new DirectItemRavenMediaModel(
|
||||
// visualMedia.optLong(viewType == RavenMediaViewMode.PERMANENT ? "url_expire_at_secs" : "replay_expiring_at_us"),
|
||||
//
|
||||
// ravenMediaModel = new DirectItemModel.DirectItemRavenMediaModel(
|
||||
// visualMedia.optLong(viewType == RavenMediaViewType.PERMANENT ? "url_expire_at_secs" : "replay_expiring_at_us"),
|
||||
// visualMedia.optInt("playback_duration_secs"),
|
||||
// visualMedia.getInt("seen_count"),
|
||||
// seenUserIds,
|
||||
@ -420,14 +456,16 @@ public final class ResponseBodyUtils {
|
||||
//
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case VIDEO_CALL_EVENT: {
|
||||
// final JSONObject videoCallEvent = itemObject.getJSONObject("video_call_event");
|
||||
// mediaModel = new DirectItemVideoCallEventModel(videoCallEvent.getLong("vc_id"),
|
||||
// videoCallEvent.optBoolean("thread_has_audio_only_call"),
|
||||
// videoCallEvent.getString("action"),
|
||||
// videoCallEvent.getString("description"));
|
||||
// videoCallEventModel = new DirectItemModel.DirectItemVideoCallEventModel(videoCallEvent.optLong("vc_id"),
|
||||
// videoCallEvent.optBoolean("thread_has_audio_only_call"),
|
||||
// videoCallEvent.getString("action"),
|
||||
// videoCallEvent.getString("description"));
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case PROFILE: {
|
||||
// final JSONObject profile = itemObject.getJSONObject("profile");
|
||||
// profileModel = new ProfileModel(profile.getBoolean("is_private"),
|
||||
@ -438,13 +476,15 @@ public final class ResponseBodyUtils {
|
||||
// profile.getString("full_name"),
|
||||
// null, null,
|
||||
// profile.getString("profile_pic_url"),
|
||||
// null, 0, 0, 0, false, false, false, false);
|
||||
// null, 0, 0, 0, false, false, false, false, false);
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case PLACEHOLDER:
|
||||
// final JSONObject placeholder = itemObject.getJSONObject("placeholder");
|
||||
// text = placeholder.getString("title") + "<br><small>" + placeholder.getString("message") + "</small>";
|
||||
// break;
|
||||
//
|
||||
// case ACTION_LOG:
|
||||
// if (inThreadView && itemObject.optInt("hide_in_thread", 0) != 0)
|
||||
// continue;
|
||||
@ -457,29 +497,35 @@ public final class ResponseBodyUtils {
|
||||
// + desc.substring(boldItem.getInt("start") + q * 7, boldItem.getInt("end") + q * 7)
|
||||
// + "</b>" + desc.substring(boldItem.getInt("end") + q * 7);
|
||||
// }
|
||||
// mediaModel = new DirectItemActionLogModel(desc);
|
||||
// actionLogModel = new DirectItemModel.DirectItemActionLogModel(desc);
|
||||
// break;
|
||||
//
|
||||
// case MEDIA_SHARE:
|
||||
// mediaModel = getDirectMediaModel(itemObject.getJSONObject("media_share"));
|
||||
// directMedia = getDirectMediaModel(itemObject.getJSONObject("media_share"));
|
||||
// break;
|
||||
//
|
||||
// case CLIP:
|
||||
// mediaModel = getDirectMediaModel(itemObject.getJSONObject("clip").getJSONObject("clip"));
|
||||
// directMedia = getDirectMediaModel(itemObject.getJSONObject("clip").getJSONObject("clip"));
|
||||
// break;
|
||||
//
|
||||
// case FELIX_SHARE:
|
||||
// mediaModel = getDirectMediaModel(itemObject.getJSONObject("felix_share").getJSONObject("video"));
|
||||
// directMedia = getDirectMediaModel(itemObject.getJSONObject("felix_share").getJSONObject("video"));
|
||||
// break;
|
||||
//
|
||||
// case MEDIA:
|
||||
// mediaModel = getDirectMediaModel(itemObject.optJSONObject("media"));
|
||||
// directMedia = getDirectMediaModel(itemObject.optJSONObject("media"));
|
||||
// break;
|
||||
//
|
||||
// case LIKE:
|
||||
// text = itemObject.getString("like");
|
||||
// break;
|
||||
//
|
||||
// case STORY_SHARE:
|
||||
// final JSONObject storyShare = itemObject.getJSONObject("story_share");
|
||||
// if (!storyShare.has("media"))
|
||||
// text = "<small>" + storyShare.optString("message") + "</small>";
|
||||
// else {
|
||||
// mediaModel = new DirectItemReelShareModel(
|
||||
// reelShareModel = new DirectItemModel.DirectItemReelShareModel(
|
||||
// storyShare.optBoolean("is_reel_persisted"),
|
||||
// storyShare.getJSONObject("media").getJSONObject("user").getLong("pk"),
|
||||
// storyShare.getJSONObject("media").getJSONObject("user").getString("username"),
|
||||
@ -491,6 +537,7 @@ public final class ResponseBodyUtils {
|
||||
// getDirectMediaModel(storyShare.optJSONObject("media")));
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case TEXT:
|
||||
// if (!itemObject.has("text"))
|
||||
// Log.d("AWAISKING_APP", "itemObject: " + itemObject); // todo
|
||||
@ -514,49 +561,40 @@ public final class ResponseBodyUtils {
|
||||
// liked,
|
||||
// itemType,
|
||||
// text,
|
||||
// linkModel,
|
||||
// profileModel,
|
||||
// mediaModel));
|
||||
// reelShareModel,
|
||||
// directMedia,
|
||||
// actionLogModel,
|
||||
// voiceMediaModel,
|
||||
// ravenMediaModel,
|
||||
// videoCallEventModel,
|
||||
// animatedMediaModel));
|
||||
// }
|
||||
//
|
||||
// return new InboxThreadModel(readState,
|
||||
// data.getString("thread_id"),
|
||||
// data.getString("thread_v2_id"),
|
||||
// data.getString("thread_type"),
|
||||
// data.getString("thread_title"),
|
||||
// data.optString("newest_cursor"),
|
||||
// data.optString("oldest_cursor"),
|
||||
// data.has("next_cursor") ? data.getString("next_cursor") : null,
|
||||
// data.has("prev_cursor") ? data.getString("prev_cursor") : null,
|
||||
// itemModels.trimToSize();
|
||||
//
|
||||
// return new InboxThreadModel(readState, threadId, threadV2Id, threadType, threadTitle,
|
||||
// threadNewestCursor, threadOldestCursor, threadNextCursor, threadPrevCursor,
|
||||
// null, // todo
|
||||
// users,
|
||||
// leftUsers,
|
||||
// adminIDs,
|
||||
// itemModels,
|
||||
// data.optBoolean("muted"),
|
||||
// data.optBoolean("is_pin"),
|
||||
// data.optBoolean("named"),
|
||||
// data.optBoolean("canonical"),
|
||||
// data.optBoolean("pending"),
|
||||
// data.getBoolean("has_older"),
|
||||
// data.optLong("read_state", 0),
|
||||
// data.optBoolean("is_spam"),
|
||||
// data.optBoolean("is_group"),
|
||||
// data.optBoolean("archived"),
|
||||
// data.optLong("last_activity_at"));
|
||||
// userModels, leftuserModels, adminIDs,
|
||||
// itemModels.toArray(new DirectItemModel[0]),
|
||||
// muted, isPin, named, canonical,
|
||||
// pending, threadHasOlder, unreadCount, isSpam, isGroup, archived, lastActivityAt);
|
||||
// }
|
||||
|
||||
// private static ActionType getExpiringMediaType(final String type) {
|
||||
// if ("raven_sent".equals(type)) return ActionType.SENT;
|
||||
// if ("raven_opened".equals(type)) return ActionType.OPENED;
|
||||
// if ("raven_blocked".equals(type)) return ActionType.BLOCKED;
|
||||
// if ("raven_sending".equals(type)) return ActionType.SENDING;
|
||||
// if ("raven_replayed".equals(type)) return ActionType.REPLAYED;
|
||||
// if ("raven_delivered".equals(type)) return ActionType.DELIVERED;
|
||||
// if ("raven_suggested".equals(type)) return ActionType.SUGGESTED;
|
||||
// if ("raven_screenshot".equals(type)) return ActionType.SCREENSHOT;
|
||||
// if ("raven_cannot_deliver".equals(type)) return ActionType.CANNOT_DELIVER;
|
||||
//
|
||||
// private static RavenExpiringMediaType getExpiringMediaType(final String type) {
|
||||
// if ("raven_sent".equals(type)) return RavenExpiringMediaType.RAVEN_SENT;
|
||||
// if ("raven_opened".equals(type)) return RavenExpiringMediaType.RAVEN_OPENED;
|
||||
// if ("raven_blocked".equals(type)) return RavenExpiringMediaType.RAVEN_BLOCKED;
|
||||
// if ("raven_sending".equals(type)) return RavenExpiringMediaType.RAVEN_SENDING;
|
||||
// if ("raven_replayed".equals(type)) return RavenExpiringMediaType.RAVEN_REPLAYED;
|
||||
// if ("raven_delivered".equals(type)) return RavenExpiringMediaType.RAVEN_DELIVERED;
|
||||
// if ("raven_suggested".equals(type)) return RavenExpiringMediaType.RAVEN_SUGGESTED;
|
||||
// if ("raven_screenshot".equals(type)) return RavenExpiringMediaType.RAVEN_SCREENSHOT;
|
||||
// if ("raven_cannot_deliver".equals(type)) return RavenExpiringMediaType.RAVEN_CANNOT_DELIVER;
|
||||
// //if ("raven_unknown".equals(type)) [default?]
|
||||
// return ActionType.UNKNOWN;
|
||||
// return RavenExpiringMediaType.RAVEN_UNKNOWN;
|
||||
// }
|
||||
|
||||
public static FeedModel parseItem(final JSONObject itemJson) throws JSONException {
|
||||
@ -590,6 +628,7 @@ public final class ResponseBodyUtils {
|
||||
0,
|
||||
0,
|
||||
following,
|
||||
false,
|
||||
restricted,
|
||||
false,
|
||||
requested);
|
||||
@ -606,7 +645,8 @@ public final class ResponseBodyUtils {
|
||||
.setPostId(itemJson.getString(Constants.EXTRAS_ID))
|
||||
.setThumbnailUrl(mediaType != MediaItemType.MEDIA_TYPE_SLIDER ? ResponseBodyUtils.getLowQualityImage(itemJson) : null)
|
||||
.setShortCode(itemJson.getString("code"))
|
||||
.setPostCaption(captionJson != null ? captionJson.optString("text") : null)
|
||||
.setPostCaption(captionJson != null ? captionJson.optString("text") : "")
|
||||
.setCaptionId(captionJson != null ? captionJson.optString("pk") : null)
|
||||
.setCommentsCount(itemJson.optInt("comment_count"))
|
||||
.setTimestamp(itemJson.optLong("taken_at", -1))
|
||||
.setLiked(itemJson.optBoolean("has_liked"))
|
||||
@ -627,7 +667,9 @@ public final class ResponseBodyUtils {
|
||||
break;
|
||||
case MEDIA_TYPE_SLIDER:
|
||||
final List<PostChild> childPosts = getChildPosts(itemJson);
|
||||
feedModelBuilder.setSliderItems(childPosts);
|
||||
feedModelBuilder.setSliderItems(childPosts)
|
||||
.setImageHeight(childPosts.get(0).getHeight())
|
||||
.setImageWidth(childPosts.get(0).getWidth());
|
||||
break;
|
||||
}
|
||||
return feedModelBuilder.build();
|
||||
@ -673,6 +715,7 @@ public final class ResponseBodyUtils {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
|
||||
@ -743,7 +786,9 @@ public final class ResponseBodyUtils {
|
||||
final JSONArray children = sidecar.optJSONArray("edges");
|
||||
if (children != null) {
|
||||
final List<PostChild> sliderItems = getSliderItems(children);
|
||||
feedModelBuilder.setSliderItems(sliderItems);
|
||||
feedModelBuilder.setSliderItems(sliderItems)
|
||||
.setImageHeight(sliderItems.get(0).getHeight())
|
||||
.setImageWidth(sliderItems.get(0).getWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -832,6 +877,134 @@ public final class ResponseBodyUtils {
|
||||
return sliderItems;
|
||||
}
|
||||
|
||||
public static StoryModel parseStoryItem(final JSONObject data,
|
||||
final boolean isLoc,
|
||||
final boolean isHashtag,
|
||||
final String localUsername) throws JSONException {
|
||||
final boolean isVideo = data.has("video_duration");
|
||||
final StoryModel model = new StoryModel(data.getString("id"),
|
||||
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0)
|
||||
.getString("url"), null,
|
||||
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
|
||||
data.optLong("taken_at", 0),
|
||||
(isLoc || isHashtag)
|
||||
? data.getJSONObject("user").getString("username")
|
||||
: localUsername,
|
||||
data.getJSONObject("user").getString("pk"),
|
||||
data.optBoolean("can_reply"));
|
||||
|
||||
if (data.getJSONObject("image_versions2").getJSONArray("candidates").length() > 1) {
|
||||
model.setThumbnail(data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(1)
|
||||
.getString("url"));
|
||||
}
|
||||
|
||||
final JSONArray videoResources = data.optJSONArray("video_versions");
|
||||
if (isVideo && videoResources != null)
|
||||
model.setVideoUrl(ResponseBodyUtils.getHighQualityPost(videoResources, true, true, false));
|
||||
|
||||
if (data.has("story_feed_media")) {
|
||||
model.setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_code"));
|
||||
}
|
||||
|
||||
// TODO: this may not be limited to spotify
|
||||
if (!data.isNull("story_app_attribution"))
|
||||
model.setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
|
||||
|
||||
if (data.has("story_polls")) {
|
||||
final JSONArray storyPolls = data.optJSONArray("story_polls");
|
||||
JSONObject tappableObject = null;
|
||||
if (storyPolls != null) {
|
||||
tappableObject = storyPolls.getJSONObject(0).optJSONObject("poll_sticker");
|
||||
}
|
||||
if (tappableObject != null) model.setPoll(new PollModel(
|
||||
String.valueOf(tappableObject.getLong("poll_id")),
|
||||
tappableObject.getString("question"),
|
||||
tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"),
|
||||
tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"),
|
||||
tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"),
|
||||
tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"),
|
||||
tappableObject.optInt("viewer_vote", -1)
|
||||
));
|
||||
}
|
||||
if (data.has("story_questions")) {
|
||||
final JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0)
|
||||
.optJSONObject("question_sticker");
|
||||
if (tappableObject != null && !tappableObject.getString("question_type").equals("music"))
|
||||
model.setQuestion(new QuestionModel(
|
||||
String.valueOf(tappableObject.getLong("question_id")),
|
||||
tappableObject.getString("question")
|
||||
));
|
||||
}
|
||||
if (data.has("story_quizs")) {
|
||||
JSONObject tappableObject = data.getJSONArray("story_quizs").getJSONObject(0).optJSONObject("quiz_sticker");
|
||||
if (tappableObject != null) {
|
||||
String[] choices = new String[tappableObject.getJSONArray("tallies").length()];
|
||||
Long[] counts = new Long[choices.length];
|
||||
for (int q = 0; q < choices.length; ++q) {
|
||||
JSONObject tempchoice = tappableObject.getJSONArray("tallies").getJSONObject(q);
|
||||
choices[q] = (q == tappableObject.getInt("correct_answer") ? "*** " : "")
|
||||
+ tempchoice.getString("text");
|
||||
counts[q] = tempchoice.getLong("count");
|
||||
}
|
||||
model.setQuiz(new QuizModel(
|
||||
String.valueOf(tappableObject.getLong("quiz_id")),
|
||||
tappableObject.getString("question"),
|
||||
choices,
|
||||
counts,
|
||||
tappableObject.optInt("viewer_answer", -1)
|
||||
));
|
||||
}
|
||||
}
|
||||
if (data.has("story_cta") && data.has("link_text")) {
|
||||
JSONObject tappableObject = data.getJSONArray("story_cta").getJSONObject(0).getJSONArray("links").getJSONObject(0);
|
||||
String swipeUpUrl = tappableObject.getString("webUri");
|
||||
if (swipeUpUrl.startsWith("http")) {
|
||||
model.setSwipeUp(new SwipeUpModel(swipeUpUrl, data.getString("link_text")));
|
||||
}
|
||||
}
|
||||
if (data.has("story_sliders")) {
|
||||
final JSONObject tappableObject = data.getJSONArray("story_sliders").getJSONObject(0)
|
||||
.optJSONObject("slider_sticker");
|
||||
if (tappableObject != null)
|
||||
model.setSlider(new SliderModel(
|
||||
String.valueOf(tappableObject.getLong("slider_id")),
|
||||
tappableObject.getString("question"),
|
||||
tappableObject.getString("emoji"),
|
||||
tappableObject.getBoolean("viewer_can_vote"),
|
||||
tappableObject.getDouble("slider_vote_average"),
|
||||
tappableObject.getInt("slider_vote_count"),
|
||||
tappableObject.optDouble("viewer_vote")
|
||||
));
|
||||
}
|
||||
JSONArray hashtags = data.optJSONArray("story_hashtags");
|
||||
JSONArray locations = data.optJSONArray("story_locations");
|
||||
JSONArray atmarks = data.optJSONArray("reel_mentions");
|
||||
String[] mentions = new String[(hashtags == null ? 0 : hashtags.length())
|
||||
+ (atmarks == null ? 0 : atmarks.length())
|
||||
+ (locations == null ? 0 : locations.length())];
|
||||
if (hashtags != null) {
|
||||
for (int h = 0; h < hashtags.length(); ++h) {
|
||||
mentions[h] = "#" + hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name");
|
||||
}
|
||||
}
|
||||
if (atmarks != null) {
|
||||
for (int h = 0; h < atmarks.length(); ++h) {
|
||||
mentions[h + (hashtags == null ? 0 : hashtags.length())] =
|
||||
"@" + atmarks.getJSONObject(h).getJSONObject("user").getString("username");
|
||||
}
|
||||
}
|
||||
if (locations != null) {
|
||||
for (int h = 0; h < locations.length(); ++h) {
|
||||
mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] =
|
||||
locations.getJSONObject(h).getJSONObject("location").getString("short_name")
|
||||
+ " (" + locations.getJSONObject(h).getJSONObject("location").getLong("pk") + ")";
|
||||
}
|
||||
}
|
||||
if (mentions.length != 0) model.setMentions(mentions);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public static String getThumbUrl(final ImageVersions2 imageVersions2) {
|
||||
if (imageVersions2 == null) return null;
|
||||
final List<MediaCandidate> candidates = imageVersions2.getCandidates();
|
||||
|
@ -41,6 +41,7 @@ import static awais.instagrabber.utils.Constants.PREF_TOPIC_POSTS_LAYOUT;
|
||||
import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION;
|
||||
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
|
||||
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
|
||||
import static awais.instagrabber.utils.Constants.STORY_SORT;
|
||||
import static awais.instagrabber.utils.Constants.SWAP_DATE_TIME_FORMAT_ENABLED;
|
||||
|
||||
public final class SettingsHelper {
|
||||
@ -123,7 +124,7 @@ public final class SettingsHelper {
|
||||
{APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT,
|
||||
DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT,
|
||||
PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, PREF_LOCATION_POSTS_LAYOUT,
|
||||
PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, PREF_EMOJI_VARIANTS})
|
||||
PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, STORY_SORT, PREF_EMOJI_VARIANTS})
|
||||
public @interface StringSettings {}
|
||||
|
||||
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
|
||||
|
@ -94,12 +94,9 @@ public final class Utils {
|
||||
if (signed == null) {
|
||||
return null;
|
||||
}
|
||||
final String[] parts = signed.split("&");
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
for (final String part : parts) {
|
||||
final String[] partSplit = part.split("=");
|
||||
map.put(partSplit[0], partSplit[1]);
|
||||
}
|
||||
map.put("ig_sig_key_version", Constants.SIGNATURE_VERSION);
|
||||
map.put("signed_body", signed.split("&signed_body=")[1]);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -16,4 +16,12 @@ public class BasePostViewModel<T extends BasePostModel> extends ViewModel {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void edit(int index, T post) {
|
||||
|
||||
}
|
||||
|
||||
public void delete(int index) {
|
||||
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package awais.instagrabber.viewmodels;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.models.FeedStoryModel;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -6,16 +6,19 @@ import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.repositories.DirectMessagesRepository;
|
||||
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions.ThreadIdOrUserIds;
|
||||
import awais.instagrabber.repositories.requests.directmessages.LinkBroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.PhotoBroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.StoryReplyBroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.TextBroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.VideoBroadcastOptions;
|
||||
import awais.instagrabber.repositories.requests.directmessages.VoiceBroadcastOptions;
|
||||
@ -146,6 +149,12 @@ public class DirectMessagesService extends BaseService {
|
||||
return broadcast(new VoiceBroadcastOptions(clientContext, threadIdOrUserIds, uploadId, waveform, samplingFreq));
|
||||
}
|
||||
|
||||
public Call<DirectThreadBroadcastResponse> broadcastStoryReply(final ThreadIdOrUserIds threadIdOrUserIds,
|
||||
final String text,
|
||||
final String mediaId, final String reelId) throws UnsupportedEncodingException {
|
||||
return broadcast(new StoryReplyBroadcastOptions(UUID.randomUUID().toString(), threadIdOrUserIds, text, mediaId, reelId));
|
||||
}
|
||||
|
||||
private Call<DirectThreadBroadcastResponse> broadcast(@NonNull final BroadcastOptions broadcastOptions) {
|
||||
if (TextUtils.isEmpty(broadcastOptions.getClientContext())) {
|
||||
throw new IllegalArgumentException("Broadcast requires a valid client context value");
|
||||
|
@ -165,6 +165,7 @@ public class DiscoverService extends BaseService {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
final String resourceUrl = ResponseBodyUtils.getHighQualityImage(coverMediaJson);
|
||||
|
@ -19,6 +19,8 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostChild;
|
||||
@ -36,7 +38,6 @@ import retrofit2.Retrofit;
|
||||
|
||||
public class FeedService extends BaseService {
|
||||
private static final String TAG = "FeedService";
|
||||
private static final boolean loadFromMock = false;
|
||||
|
||||
private final FeedRepository repository;
|
||||
|
||||
@ -44,7 +45,7 @@ public class FeedService extends BaseService {
|
||||
|
||||
private FeedService() {
|
||||
final Retrofit retrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://www.instagram.com")
|
||||
.baseUrl("https://i.instagram.com")
|
||||
.build();
|
||||
repository = retrofit.create(FeedRepository.class);
|
||||
}
|
||||
@ -56,41 +57,26 @@ public class FeedService extends BaseService {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void fetch(final int maxItemsToLoad,
|
||||
public void fetch(final String csrfToken,
|
||||
final String cursor,
|
||||
final ServiceCallback<PostsFetchResponse> callback) {
|
||||
if (loadFromMock) {
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(() -> {
|
||||
final ClassLoader classLoader = getClass().getClassLoader();
|
||||
if (classLoader == null) {
|
||||
Log.e(TAG, "fetch: classLoader is null!");
|
||||
return;
|
||||
}
|
||||
try (InputStream resourceAsStream = classLoader.getResourceAsStream("feed_response.json");
|
||||
Reader in = new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8)) {
|
||||
final int bufferSize = 1024;
|
||||
final char[] buffer = new char[bufferSize];
|
||||
final StringBuilder out = new StringBuilder();
|
||||
int charsRead;
|
||||
while ((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
|
||||
out.append(buffer, 0, charsRead);
|
||||
}
|
||||
callback.onSuccess(parseResponseBody(out.toString()));
|
||||
} catch (IOException | JSONException e) {
|
||||
Log.e(TAG, "fetch: ", e);
|
||||
}
|
||||
}, 1000);
|
||||
return;
|
||||
final Map<String, String> form = new HashMap<>();
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("phone_id", UUID.randomUUID().toString());
|
||||
form.put("device_id", UUID.randomUUID().toString());
|
||||
form.put("client_session_id", UUID.randomUUID().toString());
|
||||
form.put("is_prefetch", "0");
|
||||
form.put("timezone_offset", String.valueOf(TimeZone.getDefault().getRawOffset() / 1000));
|
||||
if (!TextUtils.isEmpty(cursor)) {
|
||||
form.put("max_id", cursor);
|
||||
form.put("reason", "pagination");
|
||||
}
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("query_hash", "c699b185975935ae2a457f24075de8c7");
|
||||
queryMap.put("variables", "{" +
|
||||
"\"fetch_media_item_count\":" + maxItemsToLoad + "," +
|
||||
"\"fetch_like\":3,\"has_stories\":false,\"has_stories\":false,\"has_threaded_comments\":true," +
|
||||
"\"fetch_media_item_cursor\":\"" + (cursor == null ? "" : cursor) + "\"" +
|
||||
"}");
|
||||
final Call<String> request = repository.fetch(queryMap);
|
||||
else {
|
||||
form.put("is_pull_to_refresh", "1");
|
||||
form.put("reason", "pull_to_refresh");
|
||||
}
|
||||
final Call<String> request = repository.fetch(form);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -130,35 +116,45 @@ public class FeedService extends BaseService {
|
||||
@NonNull
|
||||
private PostsFetchResponse parseResponseBody(@NonNull final String body)
|
||||
throws JSONException {
|
||||
final JSONObject root = new JSONObject(body);
|
||||
final boolean moreAvailable = root.optBoolean("more_available");
|
||||
String nextMaxId = root.optString("next_max_id");
|
||||
final boolean needNewMaxId = nextMaxId.equals("feed_recs_head_load");
|
||||
final JSONArray feedItems = root.optJSONArray("items");
|
||||
final List<FeedModel> feedModels = new ArrayList<>();
|
||||
final JSONObject timelineFeed = new JSONObject(body)
|
||||
.getJSONObject("data")
|
||||
.getJSONObject(Constants.EXTRAS_USER)
|
||||
.getJSONObject("edge_web_feed_timeline");
|
||||
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) {
|
||||
if (itemJson == null || itemJson.has("injected")) {
|
||||
continue;
|
||||
}
|
||||
final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson);
|
||||
if (feedModel != null) {
|
||||
feedModels.add(feedModel);
|
||||
else if (itemJson.has("end_of_feed_demarcator") && needNewMaxId) {
|
||||
final JSONArray groups = itemJson.getJSONObject("end_of_feed_demarcator").getJSONObject("group_set").getJSONArray("groups");
|
||||
for (int j = 0; j < groups.length(); ++j) {
|
||||
final JSONObject groupJson = groups.optJSONObject(j);
|
||||
if (groupJson.getString("id").equals("past_posts")) {
|
||||
nextMaxId = groupJson.optString("next_max_id");
|
||||
final JSONArray miniFeedItems = groupJson.optJSONArray("feed_items");
|
||||
for (int k = 0; k < miniFeedItems.length(); ++k) {
|
||||
final JSONObject miniItemJson = miniFeedItems.optJSONObject(k);
|
||||
if (miniItemJson == null || miniItemJson.has("injected")) {
|
||||
continue;
|
||||
}
|
||||
final FeedModel feedModel = ResponseBodyUtils.parseItem(miniItemJson);
|
||||
if (feedModel != null) {
|
||||
feedModels.add(feedModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson);
|
||||
if (feedModel != null) {
|
||||
feedModels.add(feedModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PostsFetchResponse(feedModels, hasNextPage, endCursor);
|
||||
return new PostsFetchResponse(feedModels, moreAvailable, nextMaxId);
|
||||
}
|
||||
}
|
||||
}
|
@ -153,13 +153,12 @@ public class FriendshipService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
// log in required
|
||||
public void getList(final boolean follower,
|
||||
final String targetUserId,
|
||||
final String maxId,
|
||||
final ServiceCallback<FriendshipRepoListFetchResponse> callback) {
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("max_id", maxId == null ? "" : maxId);
|
||||
if (maxId != null) queryMap.put("max_id", maxId);
|
||||
final Call<String> request = repository.getList(Constants.I_USER_AGENT,
|
||||
targetUserId,
|
||||
follower ? "followers" : "following",
|
||||
@ -173,7 +172,6 @@ public class FriendshipService extends BaseService {
|
||||
}
|
||||
final String body = response.body();
|
||||
if (TextUtils.isEmpty(body)) {
|
||||
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.repositories.LocationRepository;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import retrofit2.Call;
|
||||
@ -29,7 +30,7 @@ import retrofit2.Retrofit;
|
||||
public class LocationService extends BaseService {
|
||||
private static final String TAG = "LocationService";
|
||||
|
||||
private final LocationRepository repository, webRepository;
|
||||
private final LocationRepository repository;
|
||||
|
||||
private static LocationService instance;
|
||||
|
||||
@ -38,10 +39,6 @@ public class LocationService extends BaseService {
|
||||
.baseUrl("https://i.instagram.com")
|
||||
.build();
|
||||
repository = retrofit.create(LocationRepository.class);
|
||||
final Retrofit webRetrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://www.instagram.com")
|
||||
.build();
|
||||
webRepository = webRetrofit.create(LocationRepository.class);
|
||||
}
|
||||
|
||||
public static LocationService getInstance() {
|
||||
@ -53,7 +50,7 @@ public class LocationService extends BaseService {
|
||||
|
||||
public void fetchPosts(@NonNull final String locationId,
|
||||
final String maxId,
|
||||
final ServiceCallback<LocationPostsFetchResponse> callback) {
|
||||
final ServiceCallback<PostsFetchResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
if (!TextUtils.isEmpty(maxId)) {
|
||||
builder.put("max_id", maxId);
|
||||
@ -71,7 +68,7 @@ public class LocationService extends BaseService {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
final LocationPostsFetchResponse tagPostsFetchResponse = parseResponse(body);
|
||||
final PostsFetchResponse tagPostsFetchResponse = parseResponse(body);
|
||||
callback.onSuccess(tagPostsFetchResponse);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "onResponse", e);
|
||||
@ -88,20 +85,16 @@ public class LocationService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
private LocationPostsFetchResponse parseResponse(@NonNull final String body) throws JSONException {
|
||||
private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException {
|
||||
final JSONObject root = new JSONObject(body);
|
||||
final boolean moreAvailable = root.optBoolean("more_available");
|
||||
final String nextMaxId = root.optString("next_max_id");
|
||||
final int numResults = root.optInt("num_results");
|
||||
final String status = root.optString("status");
|
||||
final JSONArray itemsJson = root.optJSONArray("items");
|
||||
final List<FeedModel> items = parseItems(itemsJson);
|
||||
return new LocationPostsFetchResponse(
|
||||
return new PostsFetchResponse(
|
||||
items,
|
||||
moreAvailable,
|
||||
nextMaxId,
|
||||
numResults,
|
||||
status,
|
||||
items
|
||||
nextMaxId
|
||||
);
|
||||
}
|
||||
|
||||
@ -122,174 +115,4 @@ public class LocationService extends BaseService {
|
||||
}
|
||||
return feedModels;
|
||||
}
|
||||
|
||||
public void fetchGraphQLPosts(@NonNull final String locationId,
|
||||
final String maxId,
|
||||
final ServiceCallback<LocationPostsFetchResponse> callback) {
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("query_hash", "36bd0f2bf5911908de389b8ceaa3be6d");
|
||||
queryMap.put("variables", "{" +
|
||||
"\"id\":\"" + locationId + "\"," +
|
||||
"\"first\":25," +
|
||||
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"" +
|
||||
"}");
|
||||
final Call<String> request = webRepository.fetchGraphQLPosts(queryMap);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
try {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
final String body = response.body();
|
||||
if (TextUtils.isEmpty(body)) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
final LocationPostsFetchResponse tagPostsFetchResponse = parseGraphQLResponse(body);
|
||||
callback.onSuccess(tagPostsFetchResponse);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "onResponse", e);
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private LocationPostsFetchResponse parseGraphQLResponse(@NonNull final String body) throws JSONException {
|
||||
final JSONObject rootroot = new JSONObject(body);
|
||||
final JSONObject root = rootroot.getJSONObject("data").getJSONObject("location").getJSONObject("edge_location_to_media");
|
||||
final boolean moreAvailable = root.getJSONObject("page_info").optBoolean("has_next_page");
|
||||
final String nextMaxId = root.getJSONObject("page_info").optString("end_cursor");
|
||||
final int numResults = root.optInt("count");
|
||||
final String status = rootroot.optString("status");
|
||||
final JSONArray itemsJson = root.optJSONArray("edges");
|
||||
final List<FeedModel> items = parseGraphQLItems(itemsJson);
|
||||
return new LocationPostsFetchResponse(
|
||||
moreAvailable,
|
||||
nextMaxId,
|
||||
numResults,
|
||||
status,
|
||||
items
|
||||
);
|
||||
}
|
||||
|
||||
private List<FeedModel> parseGraphQLItems(final JSONArray items) throws JSONException {
|
||||
if (items == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<FeedModel> feedModels = new ArrayList<>();
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
final JSONObject itemJson = items.optJSONObject(i);
|
||||
if (itemJson == null) {
|
||||
continue;
|
||||
}
|
||||
final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson);
|
||||
if (feedModel != null) {
|
||||
feedModels.add(feedModel);
|
||||
}
|
||||
}
|
||||
return feedModels;
|
||||
}
|
||||
|
||||
public static class LocationPostsFetchResponse {
|
||||
private boolean moreAvailable;
|
||||
private String nextMaxId;
|
||||
private int numResults;
|
||||
private String status;
|
||||
private List<FeedModel> items;
|
||||
|
||||
public LocationPostsFetchResponse(final boolean moreAvailable,
|
||||
final String nextMaxId,
|
||||
final int numResults,
|
||||
final String status,
|
||||
final List<FeedModel> items) {
|
||||
this.moreAvailable = moreAvailable;
|
||||
this.nextMaxId = nextMaxId;
|
||||
this.numResults = numResults;
|
||||
this.status = status;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public boolean isMoreAvailable() {
|
||||
return moreAvailable;
|
||||
}
|
||||
|
||||
public LocationPostsFetchResponse setMoreAvailable(final boolean moreAvailable) {
|
||||
this.moreAvailable = moreAvailable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getNextMaxId() {
|
||||
return nextMaxId;
|
||||
}
|
||||
|
||||
public LocationPostsFetchResponse setNextMaxId(final String nextMaxId) {
|
||||
this.nextMaxId = nextMaxId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getNumResults() {
|
||||
return numResults;
|
||||
}
|
||||
|
||||
public LocationPostsFetchResponse setNumResults(final int numResults) {
|
||||
this.numResults = numResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public LocationPostsFetchResponse setStatus(final String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FeedModel> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public LocationPostsFetchResponse setItems(final List<FeedModel> 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 LocationPostsFetchResponse that = (LocationPostsFetchResponse) o;
|
||||
return moreAvailable == that.moreAvailable &&
|
||||
numResults == that.numResults &&
|
||||
Objects.equals(nextMaxId, that.nextMaxId) &&
|
||||
Objects.equals(status, that.status) &&
|
||||
Objects.equals(items, that.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(moreAvailable, nextMaxId, numResults, status, items);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LocationPostsFetchResponse{" +
|
||||
"moreAvailable=" + moreAvailable +
|
||||
", nextMaxId='" + nextMaxId + '\'' +
|
||||
", numResults=" + numResults +
|
||||
", status='" + status + '\'' +
|
||||
", items=" + items +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,20 +7,25 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.repositories.MediaRepository;
|
||||
import awais.instagrabber.repositories.requests.UploadFinishOptions;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.DateUtils;
|
||||
import awais.instagrabber.utils.MediaUploadHelper;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@ -48,6 +53,37 @@ public class MediaService extends BaseService {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void fetch(final String mediaId,
|
||||
final ServiceCallback<FeedModel> callback) {
|
||||
final Call<String> request = repository.fetch(mediaId);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call,
|
||||
@NonNull final Response<String> response) {
|
||||
if (callback == null) return;
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0);
|
||||
callback.onSuccess(ResponseBodyUtils.parseItem(itemJson));
|
||||
} catch (JSONException e) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call,
|
||||
@NonNull final Throwable t) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void like(final String mediaId,
|
||||
final String userId,
|
||||
final String csrfToken,
|
||||
@ -88,7 +124,7 @@ public class MediaService extends BaseService {
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
// form.put("radio_type", "wifi-none");
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> request = repository.action(Constants.I_USER_AGENT, action, mediaId, signedForm);
|
||||
final Call<String> request = repository.action(action, mediaId, signedForm);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call,
|
||||
@ -137,7 +173,7 @@ public class MediaService extends BaseService {
|
||||
form.put("replied_to_comment_id", replyToCommentId);
|
||||
}
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> commentRequest = repository.comment(Constants.I_USER_AGENT, mediaId, signedForm);
|
||||
final Call<String> commentRequest = repository.comment(mediaId, signedForm);
|
||||
commentRequest.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -183,7 +219,7 @@ public class MediaService extends BaseService {
|
||||
form.put("_uid", userId);
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> bulkDeleteRequest = repository.commentsBulkDelete(Constants.USER_AGENT, mediaId, signedForm);
|
||||
final Call<String> bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm);
|
||||
bulkDeleteRequest.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -219,7 +255,7 @@ public class MediaService extends BaseService {
|
||||
// form.put("_uid", userId);
|
||||
// form.put("_uuid", UUID.randomUUID().toString());
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> commentLikeRequest = repository.commentLike(Constants.USER_AGENT, commentId, signedForm);
|
||||
final Call<String> commentLikeRequest = repository.commentLike(commentId, signedForm);
|
||||
commentLikeRequest.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -255,7 +291,7 @@ public class MediaService extends BaseService {
|
||||
// form.put("_uid", userId);
|
||||
// form.put("_uuid", UUID.randomUUID().toString());
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> commentUnlikeRequest = repository.commentUnlike(Constants.USER_AGENT, commentId, signedForm);
|
||||
final Call<String> commentUnlikeRequest = repository.commentUnlike(commentId, signedForm);
|
||||
commentUnlikeRequest.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
@ -283,6 +319,126 @@ public class MediaService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
public void editCaption(final String postId,
|
||||
final String userId,
|
||||
final String newCaption,
|
||||
@NonNull final String csrfToken,
|
||||
@NonNull final ServiceCallback<Boolean> callback) {
|
||||
final Map<String, Object> form = new HashMap<>();
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("_uid", userId);
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
form.put("igtv_feed_preview", "false");
|
||||
form.put("media_id", postId);
|
||||
form.put("caption_text", newCaption);
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> request = repository.editCaption(postId, signedForm);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
Log.e(TAG, "Error occurred while editing caption");
|
||||
callback.onSuccess(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final String status = jsonObject.optString("status");
|
||||
callback.onSuccess(status.equals("ok"));
|
||||
} catch (JSONException e) {
|
||||
// Log.e(TAG, "Error parsing body", e);
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Error editing caption", t);
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchLikes(final String mediaId,
|
||||
final boolean isComment,
|
||||
@NonNull final ServiceCallback<List<ProfileModel>> callback) {
|
||||
final Call<String> likesRequest = repository.fetchLikes(mediaId, isComment ? "comment_likers" : "likers");
|
||||
likesRequest.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
Log.e(TAG, "Error occurred while fetching likes of "+mediaId);
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject data = new JSONObject(body);
|
||||
final JSONArray users = data.getJSONArray("users");
|
||||
final int usersLen = users.length();
|
||||
final List<ProfileModel> userModels = new ArrayList<>();
|
||||
for (int j = 0; j < usersLen; ++j) {
|
||||
final JSONObject userObject = users.getJSONObject(j);
|
||||
userModels.add(new ProfileModel(userObject.optBoolean("is_private"),
|
||||
false,
|
||||
userObject.optBoolean("is_verified"),
|
||||
String.valueOf(userObject.get("pk")),
|
||||
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(userModels);
|
||||
} catch (JSONException e) {
|
||||
// Log.e(TAG, "Error parsing body", e);
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Error getting likes", t);
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void translate(final String id,
|
||||
final String type, // 1 caption 2 comment 3 bio
|
||||
@NonNull final ServiceCallback<String> callback) {
|
||||
final Map<String, String> form = new HashMap<>();
|
||||
form.put("id", id);
|
||||
form.put("type", type);
|
||||
final Call<String> request = repository.translate(form);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
if (body == null) {
|
||||
Log.e(TAG, "Error occurred while translating");
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final String translation = jsonObject.optString("translation");
|
||||
callback.onSuccess(translation);
|
||||
} catch (JSONException e) {
|
||||
// Log.e(TAG, "Error parsing body", e);
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Error translating", t);
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Call<String> uploadFinish(final long userId,
|
||||
@NonNull final String csrfToken,
|
||||
@NonNull final UploadFinishOptions options) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user