Merge branch 'bottombar_redesign' into bottombar_redesign

This commit is contained in:
Ammar Githam 2021-03-23 00:16:58 +09:00 committed by GitHub
commit 71e1b9c03c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
685 changed files with 47534 additions and 14814 deletions

View File

@ -42,6 +42,24 @@
"bug" "bug"
] ]
}, },
{
"login": "Zopieux",
"name": "Alexandre Macabies",
"avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
"profile": "https://github.com/Zopieux",
"contributions": [
"code"
]
},
{
"login": "MeLlamoPablo",
"name": "Pablo Rodríguez",
"avatar_url": "https://avatars.githubusercontent.com/u/11708035?v=4",
"profile": "https://github.com/MeLlamoPablo",
"contributions": [
"code"
]
},
{ {
"login": "AwaisKing", "login": "AwaisKing",
"name": "AWAiS", "name": "AWAiS",
@ -55,7 +73,7 @@
"login": "snajdovski", "login": "snajdovski",
"name": "Stefan Najdovski", "name": "Stefan Najdovski",
"avatar_url": "https://avatars2.githubusercontent.com/u/42580385?v=4", "avatar_url": "https://avatars2.githubusercontent.com/u/42580385?v=4",
"profile": "https://stefannajdovski.com/", "profile": "https://snajdovski.github.io",
"contributions": [ "contributions": [
"design", "design",
"translation" "translation"
@ -91,16 +109,6 @@
"question" "question"
] ]
}, },
{
"login": "alin-1",
"name": "ALIN",
"avatar_url": "https://avatars2.githubusercontent.com/u/13281020?v=4",
"profile": "https://alin.atwebpages.com/",
"contributions": [
"bug",
"ideas"
]
},
{ {
"login": "RickyM7", "login": "RickyM7",
"name": "Ricardo", "name": "Ricardo",
@ -131,6 +139,15 @@
"translation" "translation"
] ]
}, },
{
"login": "avtkal",
"name": "avtkal",
"avatar_url": "https://avatars.githubusercontent.com/u/63205014?v=4",
"profile": "https://github.com/avtkal",
"contributions": [
"translation"
]
},
{ {
"login": "cizordj", "login": "cizordj",
"name": "Cézar Augusto", "name": "Cézar Augusto",
@ -140,6 +157,15 @@
"translation" "translation"
] ]
}, },
{
"login": "dimitrist19",
"name": "Dimitris T",
"avatar_url": "https://avatars.githubusercontent.com/u/56406468?v=4",
"profile": "https://github.com/dimitrist19",
"contributions": [
"translation"
]
},
{ {
"login": "farzadx", "login": "farzadx",
"name": "farzadx", "name": "farzadx",
@ -194,6 +220,15 @@
"translation" "translation"
] ]
}, },
{
"login": "GenosseFlosse",
"name": "GenosseFlosse",
"avatar_url": "https://avatars.githubusercontent.com/u/59205524?v=4",
"profile": "https://github.com/GenosseFlosse",
"contributions": [
"translation"
]
},
{ {
"login": "kernoeb", "login": "kernoeb",
"name": "kernoeb", "name": "kernoeb",
@ -275,6 +310,15 @@
"translation" "translation"
] ]
}, },
{
"login": "sandboiii",
"name": "Alexey Peschany",
"avatar_url": "https://avatars.githubusercontent.com/u/17468894?v=4",
"profile": "https://gitlab.com/sandboiii",
"contributions": [
"translation"
]
},
{ {
"login": "Still34", "login": "Still34",
"name": "Still Hsu", "name": "Still Hsu",
@ -302,6 +346,15 @@
"translation" "translation"
] ]
}, },
{
"login": "wokija",
"name": "wokija",
"avatar_url": "https://avatars.githubusercontent.com/u/14982166?v=4",
"profile": "https://github.com/wokija",
"contributions": [
"translation"
]
},
{ {
"login": "ysakamoto", "login": "ysakamoto",
"name": "ysakamoto", "name": "ysakamoto",
@ -310,6 +363,15 @@
"contributions": [ "contributions": [
"translation" "translation"
] ]
},
{
"login": "ZDVokoun",
"name": "ZDVokoun",
"avatar_url": "https://avatars.githubusercontent.com/u/76393152?v=4",
"profile": "https://github.com/ZDVokoun",
"contributions": [
"translation"
]
} }
], ],
"contributorsPerLine": 6, "contributorsPerLine": 6,

View File

@ -1,19 +1,16 @@
--- ---
name: General questions & feedback name: General questions & feedback
about: These should be submitted to either one of our chatrooms, or r/barinsta on reddit. about: These should be submitted to either one of our chatrooms, the discussions, or r/barinsta on reddit.
title: "[Q]" title: "[Q]"
labels: question labels: question
assignees: '' assignees: ''
--- ---
<!-- STOP!!! STOP!!! STOP!!!
STOP!!!
General questions & feedback should be submitted to General questions & feedback should be submitted to
* one of our chatrooms (see README.md), or * one of our chatrooms (see README.md), or
* the GitHub discussions section, or
* r/barinsta on reddit. * r/barinsta on reddit.
STOP!!! STOP!!! STOP!!! STOP!!!
-->

View File

@ -36,5 +36,10 @@
<option name="name" value="maven" /> <option name="name" value="maven" />
<option name="url" value="http://maven.geotoolkit.org/" /> <option name="url" value="http://maven.geotoolkit.org/" />
</remote-repository> </remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://www.jitpack.io" />
</remote-repository>
</component> </component>
</project> </project>

View File

@ -1,4 +1,4 @@
**THERE ARE CURRENTLY NO OFFICIAL GOOGLE PLAY RELEASES. PLEASE REPORT ANY OCCURRENCES TO US.** ### THERE ARE CURRENTLY NO OFFICIAL GOOGLE PLAY RELEASES. PLEASE REPORT ANY OCCURRENCES TO US.
<img src="./app/src/main/ic_launcher-round.png" alt="Barinsta logo" align="right" width="20%"/> <img src="./app/src/main/ic_launcher-round.png" alt="Barinsta logo" align="right" width="20%"/>
@ -9,10 +9,10 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) [![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) [![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 --> [![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-32-orange.svg)](#contributors) [![All Contributors](https://img.shields.io/badge/all_contributors-39-orange.svg)](#contributors)
<!-- ALL-CONTRIBUTORS-BADGE:END --> <!-- ALL-CONTRIBUTORS-BADGE:END -->
We're previously known as InstaGrabber. Instagram client; previously known as InstaGrabber.
For documentation, visit [Barinsta.AustinHuang.me](https://barinsta.austinhuang.me). For documentation, visit [Barinsta.AustinHuang.me](https://barinsta.austinhuang.me).
@ -25,6 +25,8 @@ For documentation, visit [Barinsta.AustinHuang.me](https://barinsta.austinhuang.
Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.instagrabber.svg) vs. ![GitHub](https://img.shields.io/github/release/austinhuang0131/barinsta.svg?logo=github) Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.instagrabber.svg) vs. ![GitHub](https://img.shields.io/github/release/austinhuang0131/barinsta.svg?logo=github)
## Screenshots
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg" alt="Profile" width="15%"/></a> <a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg" alt="Profile" width="15%"/></a>
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" alt="Post" width="15%"/></a> <a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" alt="Post" width="15%"/></a>
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg" alt="Comments" width="15%"/></a> <a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg" alt="Comments" width="15%"/></a>
@ -41,7 +43,7 @@ To speed up development, we need more hands on deck. If you are proficient in Ja
* Use [GitHub issues](https://github.com/austinhuang0131/instagrabber/issues) when possible. * 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) * 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://img.shields.io/reddit/subreddit-subscribers/Barinsta?style=social)](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) * 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-@Barinsta__App-2CA5E0?logo=telegram)](https://t.me/barinsta_app) [![Discord](https://img.shields.io/badge/Discord-YtEDzN2-7289da?logo=discord&logoColor=white)](https://discord.gg/YtEDzN2)
## Contributors ## Contributors
@ -55,45 +57,54 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
<td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td> <td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
<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="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="https://github.com/Zopieux"><img src="https://avatars.githubusercontent.com/u/81353?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandre Macabies</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=Zopieux" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MeLlamoPablo"><img src="https://avatars.githubusercontent.com/u/11708035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pablo Rodríguez</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=MeLlamoPablo" title="Code">💻</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="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="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>
<tr> <tr>
<td align="center"><a href="https://snajdovski.github.io"><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="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>
<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="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://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://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://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://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>
<tr> <tr>
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/avtkal"><img src="https://avatars.githubusercontent.com/u/63205014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>avtkal</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<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/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/dimitrist19"><img src="https://avatars.githubusercontent.com/u/56406468?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dimitris T</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/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/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/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/initdebugs"><img src="https://avatars0.githubusercontent.com/u/75781464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Initdebugs</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <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>
<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://github.com/GenosseFlosse"><img src="https://avatars.githubusercontent.com/u/59205524?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GenosseFlosse</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>
</tr> </tr>
<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/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/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/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> <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/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/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/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>
<tr> <tr>
<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/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://gitlab.com/sandboiii"><img src="https://avatars.githubusercontent.com/u/17468894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexey Peschany</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>
<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/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>
</tr>
<tr>
<td align="center"><a href="https://github.com/wokija"><img src="https://avatars.githubusercontent.com/u/14982166?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wokija</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> <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>
<td align="center"><a href="https://github.com/ZDVokoun"><img src="https://avatars.githubusercontent.com/u/76393152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ZDVokoun</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr> </tr>
</table> </table>
@ -103,7 +114,7 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
## License ## License
This app's predecessor, InstaGrabber, is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber). This app's predecessor, InstaGrabber, was originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber).
Barinsta Barinsta
Copyright (C) 2020-2021 Austin Huang <im@austinhuang.me> Copyright (C) 2020-2021 Austin Huang <im@austinhuang.me>
@ -122,7 +133,7 @@ This app's predecessor, InstaGrabber, is originally made by [@AwaisKing](https:/
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
Logo by [Stefan Najdovski](https://stefannajdovski.com/). Used under license. Logo by [Stefan Najdovski](https://snajdovski.github.io/). Used under license.
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/barinsta) [![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/barinsta)
[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/barinsta) [![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/barinsta)

View File

@ -10,8 +10,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 57 versionCode 60
versionName '19.0.5' versionName '19.1.0'
multiDexEnabled true multiDexEnabled true
@ -56,13 +56,13 @@ configurations.all {
} }
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
def appcompat_version = "1.2.0" def appcompat_version = "1.2.0"
def nav_version = '2.3.2' def nav_version = '2.3.4'
def exoplayer_version = '2.12.0' def exoplayer_version = '2.13.2'
implementation 'com.google.android.material:material:1.3.0-alpha04' implementation 'com.google.android.material:material:1.4.0-alpha01'
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"
@ -70,24 +70,36 @@ dependencies {
implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version" implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation "androidx.recyclerview:recyclerview:1.2.0-beta01" implementation "androidx.recyclerview:recyclerview:1.2.0-beta02"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version" implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version" implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.preference:preference:1.1.1" implementation "androidx.preference:preference:1.1.1"
implementation "androidx.work:work-runtime:2.4.0" implementation "androidx.work:work-runtime:2.5.0"
implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.palette:palette:1.0.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation 'com.google.guava:guava:27.0.1-android' implementation 'com.google.guava:guava:27.1-jre'
// Room // Room
def room_version = "2.2.5" def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-guava:$room_version" implementation "androidx.room:room-guava:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
// CameraX
def camerax_version = "1.1.0-alpha02"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha22"
// EmojiCompat
def emoji_compat_version = "1.1.0"
implementation "androidx.emoji:emoji:$emoji_compat_version"
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
implementation 'me.austinhuang:AutoLinkTextViewV2:-SNAPSHOT' implementation 'me.austinhuang:AutoLinkTextViewV2:-SNAPSHOT'
implementation 'com.facebook.fresco:fresco:2.3.0' implementation 'com.facebook.fresco:fresco:2.3.0'
@ -99,8 +111,11 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.apache.commons:commons-imaging:1.0-alpha2' implementation 'org.apache.commons:commons-imaging:1.0-alpha2'
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
} }

View File

@ -23,6 +23,7 @@
#noinspection ShrinkerUnresolvedReference #noinspection ShrinkerUnresolvedReference
#-keep class !com.google.android.exoplayer2.**, ** { *; } #-keep class !com.google.android.exoplayer2.**, ** { *; }
#-keep class !awais.instagrabber.** { *; } -dontobfuscate
-dontobfuscate # prevent shrinking retrofit response entities
-keep class awais.instagrabber.repositories.responses.** { *; }

View File

@ -0,0 +1,161 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "0b38e12b76bb081ec837191c5ef5b54e",
"entities": [
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "cookie",
"columnName": "cookie",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fullName",
"columnName": "full_name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "profilePic",
"columnName": "profile_pic",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "favorites",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "query",
"columnName": "query_text",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "displayName",
"columnName": "display_name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "picUrl",
"columnName": "pic_url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "dateAdded",
"columnName": "date_added",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "dm_last_notified",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `thread_id` TEXT, `last_notified_msg_ts` INTEGER, `last_notified_at` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "threadId",
"columnName": "thread_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastNotifiedMsgTs",
"columnName": "last_notified_msg_ts",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastNotifiedAt",
"columnName": "last_notified_at",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_dm_last_notified_thread_id",
"unique": true,
"columnNames": [
"thread_id"
],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `${TABLE_NAME}` (`thread_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0b38e12b76bb081ec837191c5ef5b54e')"
]
}
}

View File

@ -6,6 +6,13 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
<uses-feature
android:name="android.hardware.camera.any"
android:required="false" />
<application <application
android:name=".InstaGrabberApplication" android:name=".InstaGrabberApplication"
@ -15,13 +22,13 @@
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.Light.White" android:theme="@style/AppTheme.Launcher"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity=".Main" android:taskAffinity=".Main"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -30,8 +37,8 @@
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
<!--<action android:name="android.intent.action.SEARCH" />--> <!-- <action android:name="android.intent.action.SEARCH" /> -->
<!--<action android:name="android.intent.action.WEB_SEARCH" />--> <!-- <action android:name="android.intent.action.WEB_SEARCH" /> -->
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
@ -114,6 +121,15 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" /> android:value=".activities.MainActivity" />
</activity> </activity>
<activity
android:name=".activities.CameraActivity"
android:label="Camera"
android:parentActivityName=".activities.MainActivity"
android:theme="@style/AppTheme.Light.White">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@ -131,5 +147,27 @@
<service <service
android:name=".services.DeleteImageIntentService" android:name=".services.DeleteImageIntentService"
android:exported="false" /> android:exported="false" />
<service
android:name=".services.DMSyncService"
android:exported="false" />
<receiver
android:name=".services.DMSyncAlarmReceiver"
android:enabled="true"
android:exported="false" />
<!--<receiver android:name=".services.BootCompletedReceiver" >-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
<!-- </intent-filter>-->
<!--</receiver>-->
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<meta-data
android:name="fontProviderRequests"
android:value="Noto Color Emoji Compat" />
</application> </application>
</manifest> </manifest>

View File

@ -3,6 +3,7 @@ package awais.instagrabber;
import android.app.Application; import android.app.Application;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
@ -15,13 +16,16 @@ import java.util.UUID;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.CrashReporter; import awaisomereport.CrashReporter;
import awaisomereport.LogCollector; //import awaisomereport.LogCollector;
import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER; import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER;
import static awais.instagrabber.utils.Utils.applicationHandler;
import static awais.instagrabber.utils.Utils.cacheDir;
import static awais.instagrabber.utils.Utils.clipboardManager; import static awais.instagrabber.utils.Utils.clipboardManager;
import static awais.instagrabber.utils.Utils.datetimeParser; import static awais.instagrabber.utils.Utils.datetimeParser;
import static awais.instagrabber.utils.Utils.logCollector; //import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
public final class InstaGrabberApplication extends Application { public final class InstaGrabberApplication extends Application {
@ -52,13 +56,21 @@ public final class InstaGrabberApplication extends Application {
} }
if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); if (!BuildConfig.DEBUG) CrashReporter.get(this).start();
logCollector = new LogCollector(this); // logCollector = new LogCollector(this);
CookieHandler.setDefault(NET_COOKIE_MANAGER); CookieHandler.setDefault(NET_COOKIE_MANAGER);
if (settingsHelper == null) if (settingsHelper == null)
settingsHelper = new SettingsHelper(this); settingsHelper = new SettingsHelper(this);
if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper());
}
if (cacheDir == null) {
cacheDir = getCacheDir().getAbsolutePath();
}
LocaleUtils.setLocale(getBaseContext()); LocaleUtils.setLocale(getBaseContext());
if (clipboardManager == null) if (clipboardManager == null)
@ -70,6 +82,8 @@ public final class InstaGrabberApplication extends Application {
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) : settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) :
settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale()); settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale());
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString()); if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) {
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
}
} }
} }

View File

@ -0,0 +1,276 @@
package awais.instagrabber.activities;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import awais.instagrabber.databinding.ActivityCameraBinding;
import awais.instagrabber.utils.DirectoryUtils;
import awais.instagrabber.utils.PermissionUtils;
import awais.instagrabber.utils.Utils;
public class CameraActivity extends BaseLanguageActivity {
private static final String TAG = CameraActivity.class.getSimpleName();
private static final int CAMERA_REQUEST_CODE = 100;
private static final String FILE_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS";
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US);
private ActivityCameraBinding binding;
private ImageCapture imageCapture;
private File outputDirectory;
private ExecutorService cameraExecutor;
private int displayId = -1;
private final DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(final int displayId) {}
@Override
public void onDisplayRemoved(final int displayId) {}
@Override
public void onDisplayChanged(final int displayId) {
if (displayId == CameraActivity.this.displayId) {
imageCapture.setTargetRotation(binding.getRoot().getDisplay().getRotation());
}
}
};
private DisplayManager displayManager;
private ProcessCameraProvider cameraProvider;
private int lensFacing;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityCameraBinding.inflate(LayoutInflater.from(getBaseContext()));
setContentView(binding.getRoot());
Utils.transparentStatusBar(this, true, false);
displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
outputDirectory = DirectoryUtils.getOutputMediaDirectory(this, "Camera");
cameraExecutor = Executors.newSingleThreadExecutor();
displayManager.registerDisplayListener(displayListener, null);
binding.viewFinder.post(() -> {
displayId = binding.viewFinder.getDisplay().getDisplayId();
updateUi();
checkPermissionsAndSetupCamera();
});
}
@Override
protected void onResume() {
super.onResume();
// Make sure that all permissions are still present, since the
// user could have removed them while the app was in paused state.
if (!PermissionUtils.hasCameraPerms(this)) {
PermissionUtils.requestCameraPerms(this, CAMERA_REQUEST_CODE);
}
}
@Override
public void onConfigurationChanged(@NonNull final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Redraw the camera UI controls
updateUi();
// Enable or disable switching between cameras
updateCameraSwitchButton();
}
@Override
protected void onDestroy() {
super.onDestroy();
Utils.transparentStatusBar(this, false, false);
cameraExecutor.shutdown();
displayManager.unregisterDisplayListener(displayListener);
}
private void updateUi() {
binding.cameraCaptureButton.setOnClickListener(v -> takePhoto());
// Disable the button until the camera is set up
binding.switchCamera.setEnabled(false);
// Listener for button used to switch cameras. Only called if the button is enabled
binding.switchCamera.setOnClickListener(v -> {
lensFacing = CameraSelector.LENS_FACING_FRONT == lensFacing ? CameraSelector.LENS_FACING_BACK
: CameraSelector.LENS_FACING_FRONT;
// Re-bind use cases to update selected camera
bindCameraUseCases();
});
binding.close.setOnClickListener(v -> {
setResult(Activity.RESULT_CANCELED);
finish();
});
}
private void checkPermissionsAndSetupCamera() {
if (PermissionUtils.hasCameraPerms(this)) {
setupCamera();
return;
}
PermissionUtils.requestCameraPerms(this, CAMERA_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
if (requestCode == CAMERA_REQUEST_CODE) {
if (PermissionUtils.hasCameraPerms(this)) {
setupCamera();
}
}
}
private void setupCamera() {
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
// Select lensFacing depending on the available cameras
lensFacing = -1;
if (hasBackCamera()) {
lensFacing = CameraSelector.LENS_FACING_BACK;
} else if (hasFrontCamera()) {
lensFacing = CameraSelector.LENS_FACING_FRONT;
}
if (lensFacing == -1) {
throw new IllegalStateException("Back and front camera are unavailable");
}
// Enable or disable switching between cameras
updateCameraSwitchButton();
// Build and bind the camera use cases
bindCameraUseCases();
} catch (ExecutionException | InterruptedException | CameraInfoUnavailableException e) {
Log.e(TAG, "setupCamera: ", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void bindCameraUseCases() {
final int rotation = binding.viewFinder.getDisplay().getRotation();
// CameraSelector
final CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build();
// Preview
final Preview preview = new Preview.Builder()
// Set initial target rotation
.setTargetRotation(rotation)
.build();
// ImageCapture
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
// Set initial target rotation, we will have to call this again if rotation changes
// during the lifecycle of this use case
.setTargetRotation(rotation)
.build();
cameraProvider.unbindAll();
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
preview.setSurfaceProvider(binding.viewFinder.getSurfaceProvider());
}
private void takePhoto() {
if (imageCapture == null) return;
final File photoFile = new File(outputDirectory, SIMPLE_DATE_FORMAT.format(System.currentTimeMillis()) + ".jpg");
final ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(
outputFileOptions,
cameraExecutor,
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull final ImageCapture.OutputFileResults outputFileResults) {
final Uri uri = Uri.fromFile(photoFile);
//noinspection UnstableApiUsage
final String mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(Files.getFileExtension(photoFile.getName()));
MediaScannerConnection.scanFile(
CameraActivity.this,
new String[]{photoFile.getAbsolutePath()},
new String[]{mimeType},
(path, uri1) -> {
Log.d(TAG, "onImageSaved: scan complete");
final Intent intent = new Intent();
intent.setData(uri1);
setResult(Activity.RESULT_OK, intent);
finish();
});
Log.d(TAG, "onImageSaved: " + uri);
}
@Override
public void onError(@NonNull final ImageCaptureException exception) {
Log.e(TAG, "onError: ", exception);
}
}
);
// We can only change the foreground Drawable using API level 23+ API
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// // Display flash animation to indicate that photo was captured
// final ConstraintLayout container = binding.getRoot();
// container.postDelayed(() -> {
// container.setForeground(new ColorDrawable(Color.WHITE));
// container.postDelayed(() -> container.setForeground(null), 50);
// }, 100);
// }
}
/**
* Enabled or disabled a button to switch cameras depending on the available cameras
*/
private void updateCameraSwitchButton() {
try {
binding.switchCamera.setEnabled(hasBackCamera() && hasFrontCamera());
} catch (CameraInfoUnavailableException e) {
binding.switchCamera.setEnabled(false);
}
}
/**
* Returns true if the device has an available back camera. False otherwise
*/
private boolean hasBackCamera() throws CameraInfoUnavailableException {
if (cameraProvider == null) return false;
return cameraProvider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA);
}
/**
* Returns true if the device has an available front camera. False otherwise
*/
private boolean hasFrontCamera() throws CameraInfoUnavailableException {
if (cameraProvider == null) {
return false;
}
return cameraProvider.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA);
}
}

View File

@ -22,9 +22,9 @@ import androidx.core.content.ContextCompat;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.enums.IntentModelType; import awais.instagrabber.models.enums.IntentModelType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
@ -123,7 +123,7 @@ public final class DirectDownload extends AppCompatActivity {
return; return;
} }
final String text = model.getText(); final String text = model.getText();
new PostFetcher(text, new FetchListener<FeedModel>() { new PostFetcher(text, new FetchListener<Media>() {
@Override @Override
public void doBefore() { public void doBefore() {
if (notificationManager == null) return; if (notificationManager == null) return;
@ -138,7 +138,7 @@ public final class DirectDownload extends AppCompatActivity {
} }
@Override @Override
public void onResult(final FeedModel result) { public void onResult(final Media result) {
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID); if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (result == null) { if (result == null) {
finish(); finish();

View File

@ -1,5 +1,6 @@
package awais.instagrabber.activities; package awais.instagrabber.activities;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
@ -20,19 +21,26 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.provider.FontRequest;
import androidx.emoji.text.EmojiCompat;
import androidx.emoji.text.FontRequestEmojiCompatConfig;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.NavDestination; import androidx.navigation.NavDestination;
@ -40,6 +48,7 @@ import androidx.navigation.ui.NavigationUI;
import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.ArrayList; import java.util.ArrayList;
@ -53,19 +62,27 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher; import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel; import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType; import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.services.ActivityCheckerService; import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.services.DMSyncAlarmReceiver;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.IntentUtils; import awais.instagrabber.utils.IntentUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser;
import awais.instagrabber.viewmodels.AppStateViewModel;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController; import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -93,6 +110,8 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private int firstFragmentGraphIndex; private int firstFragmentGraphIndex;
private boolean isActivityCheckerServiceBound = false; private boolean isActivityCheckerServiceBound = false;
private boolean isBackStackEmpty = false; private boolean isBackStackEmpty = false;
private boolean isLoggedIn;
private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior;
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
@ -122,10 +141,20 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
binding = ActivityMainBinding.inflate(getLayoutInflater()); binding = ActivityMainBinding.inflate(getLayoutInflater());
final String cookie = settingsHelper.getString(Constants.COOKIE); final String cookie = settingsHelper.getString(Constants.COOKIE);
CookieUtils.setupCookies(cookie); CookieUtils.setupCookies(cookie);
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
if (settingsHelper.getBoolean(Constants.FLAG_SECURE))
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
setContentView(binding.getRoot()); setContentView(binding.getRoot());
final Toolbar toolbar = binding.toolbar; final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
createNotificationChannels(); createNotificationChannels();
try {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams();
//noinspection unchecked
behavior = (HideBottomViewOnScrollBehavior<BottomNavigationView>) layoutParams.getBehavior();
} catch (Exception e) {
Log.e(TAG, "onCreate: ", e);
}
if (savedInstanceState == null) { if (savedInstanceState == null) {
setupBottomNavigationBar(true); setupBottomNavigationBar(true);
} }
@ -133,12 +162,27 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES); final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES);
if (checkUpdates) FlavorTown.updateCheck(this); if (checkUpdates) FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this); FlavorTown.changelogCheck(this);
new ViewModelProvider(this).get(AppStateViewModel.class); // Just initiate the App state here
final Intent intent = getIntent(); final Intent intent = getIntent();
handleIntent(intent); handleIntent(intent);
if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) { if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) {
bindActivityCheckerService(); bindActivityCheckerService();
} }
getSupportFragmentManager().addOnBackStackChangedListener(this); getSupportFragmentManager().addOnBackStackChangedListener(this);
// Initialise the internal map
AppExecutors.getInstance().tasksThread().execute(() -> {
EmojiParser.setup(this);
EmojiVariantManager.getInstance();
});
initEmojiCompat();
// initDmService();
}
private void initDmService() {
if (!isLoggedIn) return;
final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);
if (!enabled) return;
DMSyncAlarmReceiver.setAlarm(this);
} }
@Override @Override
@ -206,7 +250,17 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (isTaskRoot() && isBackStackEmpty) { int currentNavControllerBackStack = 2;
if (currentNavControllerLiveData != null) {
final NavController navController = currentNavControllerLiveData.getValue();
if (navController != null) {
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
if (backStack != null) {
currentNavControllerBackStack = backStack.size();
}
}
}
if (isTaskRoot() && isBackStackEmpty && currentNavControllerBackStack == 2) {
finishAfterTransition(); finishAfterTransition();
return; return;
} }
@ -227,15 +281,22 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
} }
private void createNotificationChannels() { private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext()); final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID, notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
Constants.DOWNLOAD_CHANNEL_NAME, Constants.DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT)); NotificationManager.IMPORTANCE_DEFAULT));
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID, notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
Constants.ACTIVITY_CHANNEL_NAME, Constants.ACTIVITY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT)); NotificationManager.IMPORTANCE_DEFAULT));
} notificationManager.createNotificationChannel(new NotificationChannel(Constants.DM_UNREAD_CHANNEL_ID,
Constants.DM_UNREAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
final NotificationChannel silentNotificationChannel = new NotificationChannel(Constants.SILENT_NOTIFICATIONS_CHANNEL_ID,
Constants.SILENT_NOTIFICATIONS_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW);
silentNotificationChannel.setSound(null, null);
notificationManager.createNotificationChannel(silentNotificationChannel);
} }
private void setupSuggestions() { private void setupSuggestions() {
@ -372,8 +433,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private void setupBottomNavigationBar(final boolean setDefaultFromSettings) { private void setupBottomNavigationBar(final boolean setDefaultFromSettings) {
int main_nav_ids = R.array.main_nav_ids; int main_nav_ids = R.array.main_nav_ids;
final String cookie = settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
if (!isLoggedIn) { if (!isLoggedIn) {
main_nav_ids = R.array.logged_out_main_nav_ids; main_nav_ids = R.array.logged_out_main_nav_ids;
final int selectedItemId = binding.bottomNavView.getSelectedItemId(); final int selectedItemId = binding.bottomNavView.getSelectedItemId();
@ -449,12 +508,28 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (navController == null) return; if (navController == null) return;
NavigationUI.setupWithNavController(toolbar, navController); NavigationUI.setupWithNavController(toolbar, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> { navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (destination.getId() == R.id.directMessagesThreadFragment && arguments != null) {
// Set the thread title earlier for better ux
final String title = arguments.getString("title");
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null && !TextUtils.isEmpty(title)) {
actionBar.setTitle(title);
}
}
// below is a hack to check if we are at the end of the current stack, to setup the search view // below is a hack to check if we are at the end of the current stack, to setup the search view
binding.appBarLayout.setExpanded(true, true); binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId(); final int destinationId = destination.getId();
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
setupMenu(backStack.size(), destinationId); setupMenu(backStack.size(), destinationId);
binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE); final boolean contains = SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId);
binding.bottomNavView.setVisibility(contains ? View.VISIBLE : View.GONE);
if (contains && behavior != null) {
behavior.slideUp(binding.bottomNavView);
}
// explicitly hide keyboard when we navigate
final View view = getCurrentFocus();
Utils.hideKeyboard(view);
}); });
} }
@ -491,6 +566,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
showActivityView(); showActivityView();
return; return;
} }
if (Constants.ACTION_SHOW_DM_THREAD.equals(action)) {
showThread(intent);
return;
}
if (Intent.ACTION_SEND.equals(action) && type != null) { if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.equals("text/plain")) { if (type.equals("text/plain")) {
handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT)); handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT));
@ -504,6 +583,58 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
} }
} }
private void showThread(@NonNull final Intent intent) {
final String threadId = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID);
final String threadTitle = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE);
navigateToThread(threadId, threadTitle);
}
public void navigateToThread(final String threadId, final String threadTitle) {
if (threadId == null || threadTitle == null) return;
currentNavControllerLiveData.observe(this, new Observer<NavController>() {
@Override
public void onChanged(final NavController navController) {
if (navController == null) return;
if (navController.getGraph().getId() != R.id.direct_messages_nav_graph) return;
try {
final NavDestination currentDestination = navController.getCurrentDestination();
if (currentDestination != null && currentDestination.getId() == R.id.directMessagesInboxFragment) {
// if we are already on the inbox page, navigate to the thread
// need handler.post() to wait for the fragment manager to be ready to navigate
new Handler().post(() -> {
final DirectMessageInboxFragmentDirections.ActionInboxToThread action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle);
navController.navigate(action);
});
return;
}
// add a destination change listener to navigate to thread once we are on the inbox page
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull final NavController controller,
@NonNull final NavDestination destination,
@Nullable final Bundle arguments) {
if (destination.getId() == R.id.directMessagesInboxFragment) {
final DirectMessageInboxFragmentDirections.ActionInboxToThread action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle);
controller.navigate(action);
controller.removeOnDestinationChangedListener(this);
}
}
});
// pop back stack until we reach the inbox page
navController.popBackStack(R.id.directMessagesInboxFragment, false);
} finally {
currentNavControllerLiveData.removeObserver(this);
}
}
});
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
if (selectedItemId != R.navigation.direct_messages_nav_graph) {
setBottomNavSelectedItem(R.navigation.direct_messages_nav_graph);
}
}
private void handleUrl(final String url) { private void handleUrl(final String url) {
if (url == null) return; if (url == null) return;
// Log.d(TAG, url); // Log.d(TAG, url);
@ -583,7 +714,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final NavController navController = currentNavControllerLiveData.getValue(); final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putString("hashtag", "#" + hashtag); bundle.putString("hashtag", hashtag);
navController.navigate(R.id.action_global_hashTagFragment, bundle); navController.navigate(R.id.action_global_hashTagFragment, bundle);
} }
@ -591,7 +722,9 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (currentNavControllerLiveData == null) return; if (currentNavControllerLiveData == null) return;
final NavController navController = currentNavControllerLiveData.getValue(); final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return; if (navController == null) return;
navController.navigate(R.id.action_global_notificationsViewerFragment); final Bundle bundle = new Bundle();
bundle.putString("type", "notif");
navController.navigate(R.id.action_global_notificationsViewerFragment, bundle);
} }
private void bindActivityCheckerService() { private void bindActivityCheckerService() {
@ -637,4 +770,44 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
public CollapsingToolbarLayout getCollapsingToolbarView() { public CollapsingToolbarLayout getCollapsingToolbarView() {
return binding.collapsingToolbarLayout; return binding.collapsingToolbarLayout;
} }
public AppBarLayout getAppbarLayout() {
return binding.appBarLayout;
}
public void removeLayoutTransition() {
binding.getRoot().setLayoutTransition(null);
}
public void setLayoutTransition() {
binding.getRoot().setLayoutTransition(new LayoutTransition());
}
private void initEmojiCompat() {
// Use a downloadable font for EmojiCompat
final FontRequest fontRequest = new FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs);
final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(getApplicationContext(), fontRequest);
config.setReplaceAll(true)
// .setUseEmojiAsDefaultStyle(true)
.registerInitCallback(new EmojiCompat.InitCallback() {
@Override
public void onInitialized() {
Log.i(TAG, "EmojiCompat initialized");
}
@Override
public void onFailed(@Nullable Throwable throwable) {
Log.e(TAG, "EmojiCompat initialization failed", throwable);
}
});
EmojiCompat.init(config);
}
public Toolbar getToolbar() {
return binding.toolbar;
}
} }

View File

@ -0,0 +1,407 @@
package awais.instagrabber.adapters;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.AdapterListUpdateCallback;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.AsyncListDiffer;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemActionLogViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemAnimatedMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemDefaultViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemLikeViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemLinkViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemPlaceholderViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemProfileViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemRavenMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemReelShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemStoryShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemTextViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVideoCallEventViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVoiceMediaViewHolder;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmHeaderBinding;
import awais.instagrabber.databinding.LayoutDmLikeBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.databinding.LayoutDmReelShareBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.DateUtils;
public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = DirectItemsAdapter.class.getSimpleName();
private List<DirectItem> items;
private DirectThread thread;
private DirectItemViewHolder selectedViewHolder;
private final User currentUser;
private final DirectItemCallback callback;
private final AsyncListDiffer<DirectItemOrHeader> differ;
private final DirectItemInternalLongClickListener longClickListener;
private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.date.equals(newItem.date);
}
if (oldItem.item != null && newItem.item != null) {
String oldClientContext = oldItem.item.getClientContext();
if (oldClientContext == null) {
oldClientContext = oldItem.item.getItemId();
}
String newClientContext = newItem.item.getClientContext();
if (newClientContext == null) {
newClientContext = newItem.item.getItemId();
}
return oldClientContext.equals(newClientContext);
}
return false;
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.date.equals(newItem.date);
}
final boolean timestampEqual = oldItem.item.getTimestamp() == newItem.item.getTimestamp();
final boolean bothPending = oldItem.item.isPending() == newItem.item.isPending();
final boolean reactionSame = Objects.equals(oldItem.item.getReactions(), newItem.item.getReactions());
return timestampEqual && bothPending && reactionSame;
}
};
public DirectItemsAdapter(@NonNull final User currentUser,
@NonNull final DirectThread thread,
@NonNull final DirectItemCallback callback,
@NonNull final DirectItemLongClickListener itemLongClickListener) {
this.currentUser = currentUser;
this.thread = thread;
this.callback = callback;
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
longClickListener = (position, viewHolder) -> {
if (selectedViewHolder != null) {
selectedViewHolder.setSelected(false);
}
selectedViewHolder = viewHolder;
viewHolder.setSelected(true);
itemLongClickListener.onLongClick(position);
};
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if (type == -1) {
// header
return new HeaderViewHolder(LayoutDmHeaderBinding.inflate(layoutInflater, parent, false));
}
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
final DirectItemType directItemType = DirectItemType.valueOf(type);
final DirectItemViewHolder itemViewHolder = getItemViewHolder(layoutInflater, baseBinding, directItemType);
itemViewHolder.setLongClickListener(longClickListener);
return itemViewHolder;
}
@NonNull
private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater,
final LayoutDmBaseBinding baseBinding,
@NonNull final DirectItemType directItemType) {
switch (directItemType) {
case TEXT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemTextViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LIKE: {
final LayoutDmLikeBinding binding = LayoutDmLikeBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemLikeViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LINK: {
final LayoutDmLinkBinding binding = LayoutDmLinkBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemLinkViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case ACTION_LOG: {
final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemActionLogViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case VIDEO_CALL_EVENT: {
final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemVideoCallEventViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case PLACEHOLDER: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemPlaceholderViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case ANIMATED_MEDIA: {
final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemAnimatedMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case VOICE_MEDIA: {
final LayoutDmVoiceMediaBinding binding = LayoutDmVoiceMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemVoiceMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LOCATION:
case PROFILE: {
final LayoutDmProfileBinding binding = LayoutDmProfileBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemProfileViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case MEDIA: {
final LayoutDmMediaBinding binding = LayoutDmMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case CLIP:
case FELIX_SHARE:
case MEDIA_SHARE: {
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemMediaShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case STORY_SHARE: {
final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemStoryShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case REEL_SHARE: {
final LayoutDmReelShareBinding binding = LayoutDmReelShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemReelShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case RAVEN_MEDIA: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemRavenMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
default: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemDefaultViewHolder(baseBinding, binding, currentUser, thread, callback);
}
}
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
((HeaderViewHolder) holder).bind(itemOrHeader.date);
return;
}
if (thread == null) return;
((DirectItemViewHolder) holder).bind(position, itemOrHeader.item);
}
protected DirectItemOrHeader getItem(int position) {
return differ.getCurrentList().get(position);
}
@Override
public int getItemCount() {
return differ.getCurrentList().size();
}
@Override
public int getItemViewType(final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
return -1;
}
return itemOrHeader.item.getItemType().getId();
}
@Override
public long getItemId(final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
return itemOrHeader.date.hashCode();
}
if (itemOrHeader.item.getClientContext() == null) {
return itemOrHeader.item.getItemId().hashCode();
}
return itemOrHeader.item.getClientContext().hashCode();
}
public void setThread(final DirectThread thread) {
if (thread == null) return;
this.thread = thread;
// notifyDataSetChanged();
}
public void submitList(@Nullable final List<DirectItem> list) {
if (list == null) {
differ.submitList(null);
return;
}
differ.submitList(sectionAndSort(list));
this.items = list;
}
public void submitList(@Nullable final List<DirectItem> list, @Nullable final Runnable commitCallback) {
if (list == null) {
differ.submitList(null, commitCallback);
return;
}
differ.submitList(sectionAndSort(list), commitCallback);
this.items = list;
}
private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) {
final List<DirectItemOrHeader> itemOrHeaders = new ArrayList<>();
Date prevSectionDate = null;
for (int i = 0; i < list.size(); i++) {
final DirectItem item = list.get(i);
if (item == null) continue;
final DirectItemOrHeader prev = itemOrHeaders.isEmpty() ? null : itemOrHeaders.get(itemOrHeaders.size() - 1);
if (prev != null && prev.item != null && DateUtils.isSameDay(prev.item.getDate(), item.getDate())) {
// just add item
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.item = item;
itemOrHeaders.add(itemOrHeader);
if (i == list.size() - 1) {
// add header
final DirectItemOrHeader itemOrHeader2 = new DirectItemOrHeader();
itemOrHeader2.date = prevSectionDate;
itemOrHeaders.add(itemOrHeader2);
}
continue;
}
if (prevSectionDate != null) {
// add header
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.date = prevSectionDate;
itemOrHeaders.add(itemOrHeader);
}
// Add item
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.item = item;
itemOrHeaders.add(itemOrHeader);
prevSectionDate = DateUtils.dateAtZeroHours(item.getDate());
}
return itemOrHeaders;
}
public List<DirectItemOrHeader> getList() {
return differ.getCurrentList();
}
public List<DirectItem> getItems() {
return items;
}
@Override
public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) {
if (holder instanceof DirectItemViewHolder) {
((DirectItemViewHolder) holder).cleanup();
}
}
@Override
public void onViewDetachedFromWindow(@NonNull final RecyclerView.ViewHolder holder) {
if (holder instanceof DirectItemViewHolder) {
((DirectItemViewHolder) holder).cleanup();
}
}
public DirectThread getThread() {
return thread;
}
public static class DirectItemOrHeader {
Date date;
public DirectItem item;
public boolean isHeader() {
return date != null;
}
@NonNull
@Override
public String toString() {
return "DirectItemOrHeader{" +
"date=" + date +
", item=" + (item != null ? item.getItemType() : null) +
'}';
}
}
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final LayoutDmHeaderBinding binding;
public HeaderViewHolder(@NonNull final LayoutDmHeaderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final Date date) {
if (date == null) {
binding.header.setText("");
return;
}
binding.header.setText(DateFormat.getDateFormat(itemView.getContext()).format(date));
}
}
public interface DirectItemCallback {
void onHashtagClick(String hashtag);
void onMentionClick(String mention);
void onLocationClick(long locationId);
void onURLClick(String url);
void onEmailClick(String email);
void onMediaClick(Media media);
void onStoryClick(DirectItemStoryShare storyShare);
void onReaction(DirectItem item, Emoji emoji);
void onReactionClick(DirectItem item, int position);
void onOptionSelect(DirectItem item, @IdRes int itemId);
}
public interface DirectItemInternalLongClickListener {
void onLongClick(int position, DirectItemViewHolder viewHolder);
}
public interface DirectItemLongClickListener {
void onLongClick(int position);
}
}

View File

@ -4,51 +4,72 @@ import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder; import java.util.List;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding; import java.util.Objects;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> { import awais.instagrabber.adapters.viewholder.directmessages.DirectInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public final class DirectMessageInboxAdapter extends ListAdapter<DirectThread, DirectInboxItemViewHolder> {
private final OnItemClickListener onClickListener; private final OnItemClickListener onClickListener;
private static final DiffUtil.ItemCallback<InboxThreadModel> diffCallback = new DiffUtil.ItemCallback<InboxThreadModel>() { private static final DiffUtil.ItemCallback<DirectThread> diffCallback = new DiffUtil.ItemCallback<DirectThread>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { public boolean areItemsTheSame(@NonNull final DirectThread oldItem, @NonNull final DirectThread newItem) {
return oldItem.getThreadId().equals(newItem.getThreadId()); return oldItem.getThreadId().equals(newItem.getThreadId());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { public boolean areContentsTheSame(@NonNull final DirectThread oldThread,
return oldItem.equals(newItem); @NonNull final DirectThread newThread) {
final boolean titleEqual = oldThread.getThreadTitle().equals(newThread.getThreadTitle());
if (!titleEqual) return false;
final boolean lastSeenAtEqual = Objects.equals(oldThread.getLastSeenAt(), newThread.getLastSeenAt());
if (!lastSeenAtEqual) return false;
final List<DirectItem> oldItems = oldThread.getItems();
final List<DirectItem> newItems = newThread.getItems();
if (oldItems == null || newItems == null) return false;
if (oldItems.size() != newItems.size()) return false;
final DirectItem oldItemFirst = oldThread.getFirstDirectItem();
final DirectItem newItemFirst = newThread.getFirstDirectItem();
if (oldItemFirst == null || newItemFirst == null) return false;
final boolean idsEqual = oldItemFirst.getItemId().equals(newItemFirst.getItemId());
if (!idsEqual) return false;
return oldItemFirst.getTimestamp() == newItemFirst.getTimestamp();
} }
}; };
public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) { public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) {
super(diffCallback); super(new AsyncDifferConfig.Builder<>(diffCallback).build());
this.onClickListener = onClickListener; this.onClickListener = onClickListener;
} }
@NonNull @NonNull
@Override @Override
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { public DirectInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false); final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);
return new DirectMessageInboxItemViewHolder(binding); return new DirectInboxItemViewHolder(binding, onClickListener);
} }
@Override @Override
public void onBindViewHolder(@NonNull final DirectMessageInboxItemViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final DirectInboxItemViewHolder holder, final int position) {
final InboxThreadModel threadModel = getItem(position); final DirectThread thread = getItem(position);
if (onClickListener != null) { holder.bind(thread);
holder.itemView.setOnClickListener((v) -> onClickListener.onItemClick(threadModel)); }
}
holder.bind(threadModel); @Override
public long getItemId(final int position) {
return getItem(position).getThreadId().hashCode();
} }
public interface OnItemClickListener { public interface OnItemClickListener {
void onItemClick(final InboxThreadModel inboxThreadModel); void onItemClick(final DirectThread thread);
} }
} }

View File

@ -1,152 +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 java.util.List;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageActionLogViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageAnimatedMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageDefaultViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageItemViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageLinkViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageMediaShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessagePlaceholderViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageProfileViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageRavenMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageReelShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageStoryShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageTextViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageVideoCallEventViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageVoiceMediaViewHolder;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageItemsAdapter extends ListAdapter<DirectItemModel, DirectMessageItemViewHolder> {
private final List<ProfileModel> users;
private final List<ProfileModel> leftUsers;
private final View.OnClickListener onClickListener;
private final MentionClickListener mentionClickListener;
private static final DiffUtil.ItemCallback<DirectItemModel> diffCallback = new DiffUtil.ItemCallback<DirectItemModel>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) {
return oldItem.getItemId().equals(newItem.getItemId());
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) {
return oldItem.getItemId().equals(newItem.getItemId());
}
};
public DirectMessageItemsAdapter(final List<ProfileModel> users,
final List<ProfileModel> leftUsers,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
this.users = users;
this.leftUsers = leftUsers;
this.onClickListener = onClickListener;
this.mentionClickListener = mentionClickListener;
}
@NonNull
@Override
public DirectMessageItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final DirectItemType directItemType = DirectItemType.valueOf(type);
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
final ViewGroup itemViewParent = baseBinding.messageCard;
switch (directItemType) {
case LIKE:
case TEXT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageTextViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
}
case PLACEHOLDER: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessagePlaceholderViewHolder(baseBinding, binding, onClickListener);
}
case ACTION_LOG: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageActionLogViewHolder(baseBinding, binding, onClickListener);
}
case LINK: {
final LayoutDmLinkBinding binding = LayoutDmLinkBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageLinkViewHolder(baseBinding, binding, onClickListener);
}
case MEDIA: {
final LayoutDmMediaBinding binding = LayoutDmMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageMediaViewHolder(baseBinding, binding, onClickListener);
}
case PROFILE: {
final LayoutDmProfileBinding binding = LayoutDmProfileBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageProfileViewHolder(baseBinding, binding, onClickListener);
}
case REEL_SHARE: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageReelShareViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
}
case MEDIA_SHARE:
case FELIX_SHARE:
case CLIP: {
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageMediaShareViewHolder(baseBinding, binding, onClickListener);
}
case RAVEN_MEDIA: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageRavenMediaViewHolder(baseBinding, binding, onClickListener);
}
case STORY_SHARE: {
final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageStoryShareViewHolder(baseBinding, binding, onClickListener);
}
case VOICE_MEDIA: {
final LayoutDmVoiceMediaBinding binding = LayoutDmVoiceMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageVoiceMediaViewHolder(baseBinding, binding, onClickListener);
}
case ANIMATED_MEDIA: {
final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageAnimatedMediaViewHolder(baseBinding, binding, onClickListener);
}
case VIDEO_CALL_EVENT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageVideoCallEventViewHolder(baseBinding, binding, onClickListener);
}
default: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageDefaultViewHolder(baseBinding, binding, onClickListener);
}
}
}
@Override
public void onBindViewHolder(@NonNull final DirectMessageItemViewHolder holder, final int position) {
final DirectItemModel directItemModel = getItem(position);
holder.bind(directItemModel, users, leftUsers);
}
@Override
public int getItemViewType(final int position) {
return getItem(position).getItemType().getId();
}
}

View File

@ -1,47 +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.RecyclerView;
import java.util.List;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel;
public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final ProfileModel[] profileModels;
private final List<Long> admins;
private final View.OnClickListener onClickListener;
public DirectMessageMembersAdapter(final ProfileModel[] profileModels,
final List<Long> admins,
final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.admins = admins;
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[position];
holder.bind(model, admins, onClickListener);
}
@Override
public int getItemCount() {
return profileModels.length;
}
}

View File

@ -0,0 +1,117 @@
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 java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.directmessages.DirectPendingUserViewHolder;
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
public final class DirectPendingUsersAdapter extends ListAdapter<DirectPendingUsersAdapter.PendingUser, DirectPendingUserViewHolder> {
private static final DiffUtil.ItemCallback<PendingUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<PendingUser>() {
@Override
public boolean areItemsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
return oldItem.user.getPk() == newItem.user.getPk();
}
@Override
public boolean areContentsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
return Objects.equals(oldItem.user.getUsername(), newItem.user.getUsername()) &&
Objects.equals(oldItem.user.getFullName(), newItem.user.getFullName()) &&
Objects.equals(oldItem.requester, newItem.requester);
}
};
private final PendingUserCallback callback;
public DirectPendingUsersAdapter(final PendingUserCallback callback) {
super(DIFF_CALLBACK);
this.callback = callback;
setHasStableIds(true);
}
public void submitPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
if (requests == null || requests.getUsers() == null) {
submitList(Collections.emptyList());
return;
}
submitList(parse(requests));
}
private List<PendingUser> parse(final DirectThreadParticipantRequestsResponse requests) {
final List<User> users = requests.getUsers();
final Map<Long, String> requesterUsernames = requests.getRequesterUsernames();
return users.stream()
.map(user -> new PendingUser(user, requesterUsernames.get(user.getPk())))
.collect(Collectors.toList());
}
@NonNull
@Override
public DirectPendingUserViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmPendingUserItemBinding binding = LayoutDmPendingUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectPendingUserViewHolder(binding, callback);
}
@Override
public void onBindViewHolder(@NonNull final DirectPendingUserViewHolder holder, final int position) {
final PendingUser pendingUser = getItem(position);
holder.bind(position, pendingUser);
}
@Override
public long getItemId(final int position) {
final PendingUser item = getItem(position);
return item.user.getPk();
}
public static class PendingUser {
private final User user;
private final String requester;
private boolean inProgress;
public PendingUser(final User user, final String requester) {
this.user = user;
this.requester = requester;
}
public User getUser() {
return user;
}
public String getRequester() {
return requester;
}
public boolean isInProgress() {
return inProgress;
}
public PendingUser setInProgress(final boolean inProgress) {
this.inProgress = inProgress;
return this;
}
}
public interface PendingUserCallback {
void onClick(int position, PendingUser pendingUser);
void onApprove(int position, PendingUser pendingUser);
void onDeny(int position, PendingUser pendingUser);
}
}

View File

@ -0,0 +1,81 @@
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.List;
import awais.instagrabber.adapters.viewholder.directmessages.DirectReactionViewHolder;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
public final class DirectReactionsAdapter extends ListAdapter<DirectItemEmojiReaction, DirectReactionViewHolder> {
private static final DiffUtil.ItemCallback<DirectItemEmojiReaction> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectItemEmojiReaction>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
return oldItem.getSenderId() == newItem.getSenderId();
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
return oldItem.getEmoji().equals(newItem.getEmoji());
}
};
private final long viewerId;
private final List<User> users;
private final String itemId;
private final OnReactionClickListener onReactionClickListener;
public DirectReactionsAdapter(final long viewerId,
final List<User> users,
final String itemId,
final OnReactionClickListener onReactionClickListener) {
super(DIFF_CALLBACK);
this.viewerId = viewerId;
this.users = users;
this.itemId = itemId;
this.onReactionClickListener = onReactionClickListener;
setHasStableIds(true);
}
@NonNull
@Override
public DirectReactionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectReactionViewHolder(binding, viewerId, itemId, onReactionClickListener);
}
@Override
public void onBindViewHolder(@NonNull final DirectReactionViewHolder holder, final int position) {
final DirectItemEmojiReaction reaction = getItem(position);
if (reaction == null) return;
holder.bind(reaction, getUser(reaction.getSenderId()));
}
@Override
public long getItemId(final int position) {
return getItem(position).getSenderId();
}
@Nullable
private User getUser(final long pk) {
return users.stream()
.filter(user -> user.getPk() == pk)
.findFirst()
.orElse(null);
}
public interface OnReactionClickListener {
void onReactionClick(String itemId, DirectItemEmojiReaction reaction);
}
}

View File

@ -0,0 +1,183 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
import awais.instagrabber.databinding.ItemFavSectionHeaderBinding;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
public final class DirectUsersAdapter extends ListAdapter<DirectUsersAdapter.DirectUserOrHeader, RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_HEADER = 0;
private static final int VIEW_TYPE_USER = 1;
private static final DiffUtil.ItemCallback<DirectUserOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectUserOrHeader>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.headerTitle == newItem.headerTitle;
}
if (oldItem.user != null && newItem.user != null) {
return oldItem.user.getPk() == newItem.user.getPk();
}
return false;
}
@Override
public boolean areContentsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.headerTitle == newItem.headerTitle;
}
if (oldItem.user != null && newItem.user != null) {
return oldItem.user.getUsername().equals(newItem.user.getUsername()) &&
oldItem.user.getFullName().equals(newItem.user.getFullName());
}
return false;
}
};
private final long inviterId;
private final OnDirectUserClickListener onClickListener;
private final OnDirectUserLongClickListener onLongClickListener;
private List<Long> adminUserIds;
public DirectUsersAdapter(final long inviterId,
final OnDirectUserClickListener onClickListener,
final OnDirectUserLongClickListener onLongClickListener) {
super(DIFF_CALLBACK);
this.inviterId = inviterId;
this.onClickListener = onClickListener;
this.onLongClickListener = onLongClickListener;
setHasStableIds(true);
}
public void submitUsers(final List<User> users, final List<User> leftUsers) {
if (users == null && leftUsers == null) return;
final List<DirectUserOrHeader> userOrHeaders = combineLists(users, leftUsers);
submitList(userOrHeaders);
}
private List<DirectUserOrHeader> combineLists(final List<User> users, final List<User> leftUsers) {
final ImmutableList.Builder<DirectUserOrHeader> listBuilder = ImmutableList.builder();
if (users != null && !users.isEmpty()) {
listBuilder.add(new DirectUserOrHeader(R.string.members));
users.stream()
.map(DirectUserOrHeader::new)
.forEach(listBuilder::add);
}
if (leftUsers != null && !leftUsers.isEmpty()) {
listBuilder.add(new DirectUserOrHeader(R.string.dms_left_users));
leftUsers.stream()
.map(DirectUserOrHeader::new)
.forEach(listBuilder::add);
}
return listBuilder.build();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case VIEW_TYPE_USER:
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectUserViewHolder(binding, onClickListener, onLongClickListener);
case VIEW_TYPE_HEADER:
default:
final ItemFavSectionHeaderBinding headerBinding = ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false);
return new HeaderViewHolder(headerBinding);
}
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) {
((HeaderViewHolder) holder).bind(getItem(position).headerTitle);
return;
}
if (holder instanceof DirectUserViewHolder) {
final User user = getItem(position).user;
((DirectUserViewHolder) holder).bind(position,
user,
user != null && adminUserIds != null && adminUserIds.contains(user.getPk()),
user != null && user.getPk() == inviterId,
false,
false);
}
}
@Override
public int getItemViewType(final int position) {
final DirectUserOrHeader item = getItem(position);
return item.isHeader() ? VIEW_TYPE_HEADER : VIEW_TYPE_USER;
}
@Override
public long getItemId(final int position) {
final DirectUserOrHeader item = getItem(position);
return item.isHeader() ? item.headerTitle : item.user.getPk();
}
public void setAdminUserIds(final List<Long> adminUserIds) {
this.adminUserIds = adminUserIds;
notifyDataSetChanged();
}
public static class DirectUserOrHeader {
int headerTitle;
User user;
public DirectUserOrHeader(final int headerTitle) {
this.headerTitle = headerTitle;
}
public DirectUserOrHeader(final User user) {
this.user = user;
}
boolean isHeader() {
return headerTitle > 0;
}
}
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final ItemFavSectionHeaderBinding binding;
public HeaderViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(@StringRes final int headerTitle) {
binding.getRoot().setText(headerTitle);
}
}
public interface OnDirectUserClickListener {
void onClick(int position, User user, boolean selected);
}
public interface OnDirectUserLongClickListener {
boolean onLongClick(int position, User user);
}
}

View File

@ -10,7 +10,8 @@ import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder; import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;
import awais.instagrabber.databinding.ItemDiscoverTopicBinding; import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
import awais.instagrabber.models.TopicCluster; import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> { public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> {
private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() { private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {
@ -21,7 +22,8 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
@Override @Override
public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) { public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
return oldItem.getCoverMedia().getDisplayUrl().equals(newItem.getCoverMedia().getDisplayUrl()) final String oldThumbUrl = ResponseBodyUtils.getThumbUrl(oldItem.getCoverMedia());
return oldThumbUrl != null && oldThumbUrl.equals(ResponseBodyUtils.getThumbUrl(newItem.getCoverMedia()))
&& oldItem.getTitle().equals(newItem.getTitle()); && oldItem.getTitle().equals(newItem.getTitle());
} }
}; };
@ -38,7 +40,7 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false); final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
return new TopicClusterViewHolder(binding, onTopicClickListener); return new TopicClusterViewHolder(binding, onTopicClickListener, null);
} }
@Override @Override

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder; import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;
@ -22,43 +23,45 @@ import awais.instagrabber.databinding.ItemFeedGridBinding;
import awais.instagrabber.databinding.ItemFeedPhotoBinding; import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.databinding.ItemFeedSliderBinding; import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.databinding.ItemFeedVideoBinding; import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Media;
public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.ViewHolder> { public final class FeedAdapterV2 extends ListAdapter<Media, RecyclerView.ViewHolder> {
private static final String TAG = "FeedAdapterV2"; private static final String TAG = "FeedAdapterV2";
private final FeedItemCallback feedItemCallback; private final FeedItemCallback feedItemCallback;
private final SelectionModeCallback selectionModeCallback; private final SelectionModeCallback selectionModeCallback;
private final Set<Integer> selectedPositions = new HashSet<>(); private final Set<Integer> selectedPositions = new HashSet<>();
private final Set<FeedModel> selectedFeedModels = new HashSet<>(); private final Set<Media> selectedFeedModels = new HashSet<>();
private PostsLayoutPreferences layoutPreferences; private PostsLayoutPreferences layoutPreferences;
private boolean selectionModeActive = false; private boolean selectionModeActive = false;
private static final DiffUtil.ItemCallback<FeedModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<FeedModel>() { private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) { public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPostId().equals(newItem.getPostId()); return Objects.equals(oldItem.getPk(), newItem.getPk());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) { public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
boolean result = oldItem.getPostId().equals(newItem.getPostId()); final Caption oldItemCaption = oldItem.getCaption();
if (TextUtils.isEmpty(oldItem.getPostCaption())) { final Caption newItemCaption = newItem.getCaption();
return result ? TextUtils.isEmpty(newItem.getPostCaption()) : false; return Objects.equals(oldItem.getPk(), newItem.getPk())
} && Objects.equals(getCaptionText(oldItemCaption), getCaptionText(newItemCaption));
else { }
return result ? oldItem.getPostCaption().equals(newItem.getPostCaption()) : false;
} private String getCaptionText(final Caption caption) {
if (caption == null) return null;
return caption.getText();
} }
}; };
private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() { private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {
@Override @Override
public boolean onPostLongClick(final int position, final FeedModel feedModel) { public boolean onPostLongClick(final int position, final Media feedModel) {
if (!selectionModeActive) { if (!selectionModeActive) {
selectionModeActive = true; selectionModeActive = true;
notifyDataSetChanged(); notifyDataSetChanged();
@ -76,7 +79,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
} }
@Override @Override
public void onPostClick(final int position, final FeedModel feedModel) { public void onPostClick(final int position, final Media feedModel) {
if (!selectionModeActive) return; if (!selectionModeActive) return;
if (selectedPositions.contains(position)) { if (selectedPositions.contains(position)) {
selectedPositions.remove(position); selectedPositions.remove(position);
@ -147,7 +150,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
@Override @Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) { public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) {
final FeedModel feedModel = getItem(position); final Media feedModel = getItem(position);
if (feedModel == null) return; if (feedModel == null) return;
switch (layoutPreferences.getType()) { switch (layoutPreferences.getType()) {
case LINEAR: case LINEAR:
@ -168,7 +171,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
@Override @Override
public int getItemViewType(final int position) { public int getItemViewType(final int position) {
return getItem(position).getItemType().getId(); return getItem(position).getMediaType().getId();
} }
public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) { public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {
@ -205,43 +208,43 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
// } // }
public interface FeedItemCallback { public interface FeedItemCallback {
void onPostClick(final FeedModel feedModel, void onPostClick(final Media feedModel,
final View profilePicView, final View profilePicView,
final View mainPostImage); final View mainPostImage);
void onProfilePicClick(final FeedModel feedModel, void onProfilePicClick(final Media feedModel,
final View profilePicView); final View profilePicView);
void onNameClick(final FeedModel feedModel, void onNameClick(final Media feedModel,
final View profilePicView); final View profilePicView);
void onLocationClick(final FeedModel feedModel); void onLocationClick(final Media feedModel);
void onMentionClick(final String mention); void onMentionClick(final String mention);
void onHashtagClick(final String hashtag); void onHashtagClick(final String hashtag);
void onCommentsClick(final FeedModel feedModel); void onCommentsClick(final Media feedModel);
void onDownloadClick(final FeedModel feedModel, final int childPosition); void onDownloadClick(final Media feedModel, final int childPosition);
void onEmailClick(final String emailId); void onEmailClick(final String emailId);
void onURLClick(final String url); void onURLClick(final String url);
void onSliderClick(FeedModel feedModel, int position); void onSliderClick(Media feedModel, int position);
} }
public interface AdapterSelectionCallback { public interface AdapterSelectionCallback {
boolean onPostLongClick(final int position, FeedModel feedModel); boolean onPostLongClick(final int position, Media feedModel);
void onPostClick(final int position, FeedModel feedModel); void onPostClick(final int position, Media feedModel);
} }
public interface SelectionModeCallback { public interface SelectionModeCallback {
void onSelectionStart(); void onSelectionStart();
void onSelectionChange(final Set<FeedModel> selectedFeedModels); void onSelectionChange(final Set<Media> selectedFeedModels);
void onSelectionEnd(); void onSelectionEnd();
} }

View File

@ -2,20 +2,21 @@ package awais.instagrabber.adapters;
import android.view.View; import android.view.View;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.Media;
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback { public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
@Override @Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {} public void onPostClick(final Media media, final View profilePicView, final View mainPostImage) {}
@Override @Override
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {} public void onProfilePicClick(final Media media, final View profilePicView) {}
@Override @Override
public void onNameClick(final FeedModel feedModel, final View profilePicView) {} public void onNameClick(final Media media, final View profilePicView) {}
@Override @Override
public void onLocationClick(final FeedModel feedModel) {} public void onLocationClick(final Media media) {}
@Override @Override
public void onMentionClick(final String mention) {} public void onMentionClick(final String mention) {}
@ -24,10 +25,10 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onHashtagClick(final String hashtag) {} public void onHashtagClick(final String hashtag) {}
@Override @Override
public void onCommentsClick(final FeedModel feedModel) {} public void onCommentsClick(final Media media) {}
@Override @Override
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {} public void onDownloadClick(final Media media, final int childPosition) {}
@Override @Override
public void onEmailClick(final String emailId) {} public void onEmailClick(final String emailId) {}
@ -36,5 +37,5 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onURLClick(final String url) {} public void onURLClick(final String url) {}
@Override @Override
public void onSliderClick(final FeedModel feedModel, final int position) {} public void onSliderClick(final Media media, final int position) {}
} }

View File

@ -2,23 +2,46 @@ package awais.instagrabber.adapters;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.StoryListViewHolder; import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.TextUtils;
public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, StoryListViewHolder> { public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, StoryListViewHolder> implements Filterable {
private final OnFeedStoryClickListener listener; private final OnFeedStoryClickListener listener;
private List<FeedStoryModel> list;
private final Filter filter = new Filter() {
@Nullable
@Override
protected FilterResults performFiltering(final CharSequence filter) {
final boolean isFilterEmpty = TextUtils.isEmpty(filter);
final String query = isFilterEmpty ? null : filter.toString().toLowerCase();
for (FeedStoryModel item : list) {
if (isFilterEmpty) item.setShown(true);
else item.setShown(item.getProfileModel().getUsername().toLowerCase().contains(query));
}
return null;
}
@Override
protected void publishResults(final CharSequence constraint, final FilterResults results) {
submitList(list);
notifyDataSetChanged();
}
};
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() { private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() {
@Override @Override
@ -37,6 +60,17 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
this.listener = listener; this.listener = listener;
} }
@Override
public Filter getFilter() {
return filter;
}
@Override
public void submitList(final List<FeedStoryModel> list) {
super.submitList(list.stream().filter(i -> i.isShown()).collect(Collectors.toList()));
this.list = list;
}
@NonNull @NonNull
@Override @Override
public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {

View File

@ -0,0 +1,94 @@
package awais.instagrabber.adapters;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.Collection;
import java.util.List;
import awais.instagrabber.adapters.viewholder.FilterViewHolder;
import awais.instagrabber.databinding.ItemFilterBinding;
import awais.instagrabber.fragments.imageedit.filters.filters.Filter;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
public class FiltersAdapter extends ListAdapter<Filter<?>, FilterViewHolder> {
private static final DiffUtil.ItemCallback<Filter<?>> DIFF_CALLBACK = new DiffUtil.ItemCallback<Filter<?>>() {
@Override
public boolean areItemsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {
return oldItem.getType().equals(newItem.getType());
}
@Override
public boolean areContentsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {
return oldItem.getType().equals(newItem.getType());
}
};
private final Bitmap bitmap;
private final OnFilterClickListener onFilterClickListener;
private final Collection<GPUImageFilter> filters;
private final String originalKey;
private int selectedPosition = 0;
public FiltersAdapter(final Collection<GPUImageFilter> filters,
final String originalKey,
final Bitmap bitmap,
final OnFilterClickListener onFilterClickListener) {
super(DIFF_CALLBACK);
this.filters = filters;
this.originalKey = originalKey;
this.bitmap = bitmap;
this.onFilterClickListener = onFilterClickListener;
setHasStableIds(true);
}
@NonNull
@Override
public FilterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemFilterBinding binding = ItemFilterBinding.inflate(layoutInflater, parent, false);
return new FilterViewHolder(binding, filters, onFilterClickListener);
}
@Override
public void onBindViewHolder(@NonNull final FilterViewHolder holder, final int position) {
holder.bind(position, originalKey, bitmap, getItem(position), selectedPosition == position);
}
@Override
public long getItemId(final int position) {
return getItem(position).getLabel();
}
public void setSelected(final int position) {
final int prev = this.selectedPosition;
this.selectedPosition = position;
notifyItemChanged(position);
notifyItemChanged(prev);
}
public void setSelectedFilter(final GPUImageFilter instance) {
final List<Filter<?>> currentList = getCurrentList();
int index = -1;
for (int i = 0; i < currentList.size(); i++) {
final Filter<?> filter = currentList.get(i);
final GPUImageFilter filterInstance = filter.getInstance();
if (filterInstance.getClass() == instance.getClass()) {
index = i;
break;
}
}
if (index < 0) return;
setSelected(index);
}
public interface OnFilterClickListener {
void onClick(int position, Filter<?> filter);
}
}

View File

@ -0,0 +1,107 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.util.Log;
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 androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import java.util.Objects;
import awais.instagrabber.databinding.ItemMediaBinding;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.Utils;
public class GifItemsAdapter extends ListAdapter<GiphyGif, GifItemsAdapter.GifViewHolder> {
private static final DiffUtil.ItemCallback<GiphyGif> diffCallback = new DiffUtil.ItemCallback<GiphyGif>() {
@Override
public boolean areItemsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
return Objects.equals(oldItem.getId(), newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
return Objects.equals(oldItem.getId(), newItem.getId());
}
};
private final OnItemClickListener onItemClickListener;
public GifItemsAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public GifViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);
return new GifViewHolder(binding, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull final GifViewHolder holder, final int position) {
holder.bind(getItem(position));
}
public static class GifViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = GifViewHolder.class.getSimpleName();
private static final int size = Utils.displayMetrics.widthPixels / 3;
private final ItemMediaBinding binding;
private final OnItemClickListener onItemClickListener;
public GifViewHolder(@NonNull final ItemMediaBinding binding,
final OnItemClickListener onItemClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onItemClickListener = onItemClickListener;
binding.duration.setVisibility(View.GONE);
final GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(itemView.getResources());
builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);
binding.item.setHierarchy(builder.build());
}
public void bind(final GiphyGif item) {
if (onItemClickListener != null) {
itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));
}
final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFailure(final String id, final Throwable throwable) {
Log.e(TAG, "onFailure: ", throwable);
}
};
final ImageRequest request = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(item.getImages().getFixedHeight().getWebp()))
.setResizeOptions(ResizeOptions.forDimensions(size, size))
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setAutoPlayAnimations(true)
.setControllerListener(controllerListener);
binding.item.setController(builder.build());
}
}
public interface OnItemClickListener {
void onItemClick(GiphyGif giphyGif);
}
}

View File

@ -0,0 +1,42 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.dialogs.KeywordsFilterDialogViewHolder;
public class KeywordsFilterAdapter extends RecyclerView.Adapter<KeywordsFilterDialogViewHolder> {
private final Context context;
private final ArrayList<String> items;
public KeywordsFilterAdapter(Context context, ArrayList<String> items){
this.context = context;
this.items = items;
}
@NonNull
@Override
public KeywordsFilterDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_keyword, parent, false);
return new KeywordsFilterDialogViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull KeywordsFilterDialogViewHolder holder, int position) {
holder.bind(items, position, context, this);
}
@Override
public int getItemCount() {
return items.size();
}
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters; package awais.instagrabber.adapters;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -12,13 +11,13 @@ import java.util.List;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder; import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding; import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.User;
public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> { public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final List<ProfileModel> profileModels; private final List<User> profileModels;
private final View.OnClickListener onClickListener; private final View.OnClickListener onClickListener;
public LikesAdapter(final List<ProfileModel> profileModels, public LikesAdapter(final List<User> profileModels,
final View.OnClickListener onClickListener) { final View.OnClickListener onClickListener) {
this.profileModels = profileModels; this.profileModels = profileModels;
this.onClickListener = onClickListener; this.onClickListener = onClickListener;
@ -34,7 +33,7 @@ public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder>
@Override @Override
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
final ProfileModel model = profileModels.get(position); final User model = profileModels.get(position);
holder.bind(model, null, onClickListener); holder.bind(model, null, onClickListener);
} }

View File

@ -0,0 +1,111 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.util.Log;
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 androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import java.io.File;
import awais.instagrabber.databinding.ItemMediaBinding;
import awais.instagrabber.utils.MediaController.MediaEntry;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class MediaItemsAdapter extends ListAdapter<MediaEntry, MediaItemsAdapter.MediaItemViewHolder> {
private static final DiffUtil.ItemCallback<MediaEntry> diffCallback = new DiffUtil.ItemCallback<MediaEntry>() {
@Override
public boolean areItemsTheSame(@NonNull final MediaEntry oldItem, @NonNull final MediaEntry newItem) {
return oldItem.imageId == newItem.imageId;
}
@Override
public boolean areContentsTheSame(@NonNull final MediaEntry oldItem, @NonNull final MediaEntry newItem) {
return oldItem.imageId == newItem.imageId;
}
};
private final OnItemClickListener onItemClickListener;
public MediaItemsAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public MediaItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);
return new MediaItemViewHolder(binding, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull final MediaItemViewHolder holder, final int position) {
holder.bind(getItem(position));
}
public static class MediaItemViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = MediaItemViewHolder.class.getSimpleName();
private static final int size = Utils.displayMetrics.widthPixels / 3;
private final ItemMediaBinding binding;
private final OnItemClickListener onItemClickListener;
public MediaItemViewHolder(@NonNull final ItemMediaBinding binding,
final OnItemClickListener onItemClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onItemClickListener = onItemClickListener;
}
public void bind(final MediaEntry item) {
final Uri uri = Uri.fromFile(new File(item.path));
if (onItemClickListener != null) {
itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));
}
final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFailure(final String id, final Throwable throwable) {
Log.e(TAG, "onFailure: ", throwable);
}
};
final ImageRequest request = ImageRequestBuilder
.newBuilderWithSource(uri)
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(false)
.setResizeOptions(ResizeOptions.forDimensions(size, size))
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setControllerListener(controllerListener);
binding.item.setController(builder.build());
if (item.isVideo && item.duration >= 0) {
final String timeString = TextUtils.millisToTimeString(item.duration);
binding.duration.setVisibility(View.VISIBLE);
binding.duration.setText(timeString);
} else {
binding.duration.setVisibility(View.GONE);
}
}
}
public interface OnItemClickListener {
void onItemClick(MediaEntry entry);
}
}

View File

@ -11,34 +11,31 @@ import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder; import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification;
public final class NotificationsAdapter extends ListAdapter<NotificationModel, NotificationViewHolder> { public final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> {
private final OnNotificationClickListener notificationClickListener; private final OnNotificationClickListener notificationClickListener;
private final MentionClickListener mentionClickListener;
private static final DiffUtil.ItemCallback<NotificationModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<NotificationModel>() { private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) { public boolean areItemsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
return oldItem.getId().equals(newItem.getId()); return oldItem.getPk().equals(newItem.getPk());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) { public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
return oldItem.getId().equals(newItem.getId()); return oldItem.getPk().equals(newItem.getPk());
} }
}; };
public NotificationsAdapter(final OnNotificationClickListener notificationClickListener, public NotificationsAdapter(final OnNotificationClickListener notificationClickListener) {
final MentionClickListener mentionClickListener) {
super(DIFF_CALLBACK); super(DIFF_CALLBACK);
this.notificationClickListener = notificationClickListener; this.notificationClickListener = notificationClickListener;
this.mentionClickListener = mentionClickListener;
} }
@NonNull @NonNull
@ -51,12 +48,12 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
@Override @Override
public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {
final NotificationModel notificationModel = getItem(position); final Notification Notification = getItem(position);
holder.bind(notificationModel, notificationClickListener); holder.bind(Notification, notificationClickListener);
} }
@Override @Override
public void submitList(@Nullable final List<NotificationModel> list, @Nullable final Runnable commitCallback) { public void submitList(@Nullable final List<Notification> list, @Nullable final Runnable commitCallback) {
if (list == null) { if (list == null) {
super.submitList(null, commitCallback); super.submitList(null, commitCallback);
return; return;
@ -65,7 +62,7 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
} }
@Override @Override
public void submitList(@Nullable final List<NotificationModel> list) { public void submitList(@Nullable final List<Notification> list) {
if (list == null) { if (list == null) {
super.submitList(null); super.submitList(null);
return; return;
@ -73,8 +70,10 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
super.submitList(sort(list)); super.submitList(sort(list));
} }
private List<NotificationModel> sort(final List<NotificationModel> list) { private List<Notification> sort(final List<Notification> list) {
final List<NotificationModel> listCopy = new ArrayList<>(list); final List<Notification> listCopy = new ArrayList<>(list).stream()
.filter(i -> i.getType() != null)
.collect(Collectors.toList());
Collections.sort(listCopy, (o1, o2) -> { Collections.sort(listCopy, (o1, o2) -> {
// keep requests at top // keep requests at top
if (o1.getType() == o2.getType() if (o1.getType() == o2.getType()
@ -83,16 +82,16 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
else if (o1.getType() == NotificationType.REQUEST) return -1; else if (o1.getType() == NotificationType.REQUEST) return -1;
else if (o2.getType() == NotificationType.REQUEST) return 1; else if (o2.getType() == NotificationType.REQUEST) return 1;
// timestamp // timestamp
return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1); return Double.compare(o2.getArgs().getTimestamp(), o1.getArgs().getTimestamp());
}); });
return listCopy; return listCopy;
} }
public interface OnNotificationClickListener { public interface OnNotificationClickListener {
void onNotificationClick(final NotificationModel model); void onNotificationClick(final Notification model);
void onProfileClick(final String username); void onProfileClick(final String username);
void onPreviewClick(final NotificationModel model); void onPreviewClick(final Notification model);
} }
} }

View File

@ -1,206 +0,0 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import awais.instagrabber.customviews.drawee.ZoomableDraweeView;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public class PostViewerChildAdapter extends ListAdapter<ViewerPostModel, PostViewerChildAdapter.ChildViewHolder> {
private static final DiffUtil.ItemCallback<ViewerPostModel> diffCallback = new DiffUtil.ItemCallback<ViewerPostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final ViewerPostModel oldItem, @NonNull final ViewerPostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId()) && oldItem.getShortCode().equals(newItem.getShortCode());
}
@Override
public boolean areContentsTheSame(@NonNull final ViewerPostModel oldItem, @NonNull final ViewerPostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId()) && oldItem.getShortCode().equals(newItem.getShortCode());
}
};
public PostViewerChildAdapter() {
super(diffCallback);
}
@NonNull
@Override
public ChildViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
// final AppCompatTextView textView = new AppCompatTextView(parent.getContext());
// textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// return new ChildViewHolder(textView);
final MediaItemType mediaItemType = MediaItemType.valueOf(viewType);
if (mediaItemType == null) return getPlaceholder(parent);
switch (mediaItemType) {
case MEDIA_TYPE_IMAGE:
return getImageViewHolder(parent);
case MEDIA_TYPE_VIDEO:
return getVideoViewHolder(parent);
default:
return getPlaceholder(parent);
}
}
private ChildViewHolder getImageViewHolder(final ViewGroup parent) {
final ZoomableDraweeView view = new ZoomableDraweeView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ChildViewHolder(view);
}
private ChildViewHolder getVideoViewHolder(final ViewGroup parent) {
final PlayerView view = new PlayerView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ChildViewHolder(view);
}
private ChildViewHolder getPlaceholder(final ViewGroup parent) {
final AppCompatTextView textView = new AppCompatTextView(parent.getContext());
textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textView.setGravity(Gravity.CENTER);
textView.setText("Placeholder");
return new ChildViewHolder(textView);
}
@Override
public void onBindViewHolder(@NonNull final ChildViewHolder holder, final int position) {
holder.bind(getItem(position));
}
@Override
public int getItemViewType(final int position) {
final ViewerPostModel item = getItem(position);
return item.getItemType().getId();
}
@Override
public void onViewDetachedFromWindow(@NonNull final ChildViewHolder holder) {
if (holder.itemView instanceof PlayerView) {
final Player player = ((PlayerView) holder.itemView).getPlayer();
if (player != null) {
player.setPlayWhenReady(false);
}
}
}
@Override
public void onViewRecycled(@NonNull final ChildViewHolder holder) {
if (holder.itemView instanceof PlayerView) {
final Player player = ((PlayerView) holder.itemView).getPlayer();
if (player != null) {
player.release();
}
return;
}
if (holder.itemView instanceof ZoomableDraweeView) {
((ZoomableDraweeView) holder.itemView).setController(null);
}
}
public static class ChildViewHolder extends RecyclerView.ViewHolder {
public ChildViewHolder(@NonNull final View itemView) {
super(itemView);
}
public void bind(final ViewerPostModel item) {
final MediaItemType mediaItemType = item.getItemType();
switch (mediaItemType) {
case MEDIA_TYPE_IMAGE:
bindImage(item);
break;
case MEDIA_TYPE_VIDEO:
bindVideo(item);
break;
default:
}
}
private void bindImage(final ViewerPostModel item) {
final ZoomableDraweeView imageView = (ZoomableDraweeView) itemView;
imageView.setController(null);
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.getDisplayUrl()))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(imageView.getController())
// .setControllerListener(new BaseControllerListener<ImageInfo>() {
//
// @Override
// public void onFailure(final String id, final Throwable throwable) {
// // viewerBinding.progressView.setVisibility(View.GONE);
// }
//
// @Override
// public void onFinalImageSet(final String id, final ImageInfo imageInfo, final Animatable animatable) {
// // viewerBinding.progressView.setVisibility(View.GONE);
// }
// })
.build();
imageView.setController(controller);
}
private void bindVideo(final ViewerPostModel item) {
final SimpleExoPlayer player = new SimpleExoPlayer.Builder(itemView.getContext()).build();
final PlayerView playerView = (PlayerView) itemView;
playerView.setPlayer(player);
float vol = Utils.settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
player.setVolume(vol);
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(
new DefaultDataSourceFactory(itemView.getContext(), "instagram")
).createMediaSource(MediaItem.fromUri(item.getDisplayUrl()));
// mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
// @Override
// public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.progressView.setVisibility(View.GONE);
// }
//
// @Override
// public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.progressView.setVisibility(View.VISIBLE);
// }
//
// @Override
// public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.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, final boolean wasCanceled) {
// viewerBinding.progressView.setVisibility(View.GONE);
// }
// });
player.setMediaSource(mediaSource);
player.prepare();
player.setVolume(vol);
// viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
// viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
}
}
}

View File

@ -1,45 +0,0 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import awais.instagrabber.adapters.viewholder.PostViewHolder;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
public final class PostsAdapter extends MultiSelectListAdapter<PostModel, PostViewHolder> {
private static final DiffUtil.ItemCallback<PostModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public PostsAdapter(final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
super(DIFF_CALLBACK, clickListener, longClickListener);
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemPostBinding binding = ItemPostBinding.inflate(layoutInflater, parent, false);
return new PostViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostViewHolder holder, final int position) {
final PostModel postModel = getItem(position);
holder.bind(postModel, position, getInternalOnItemClickListener(), getInternalOnLongItemClickListener());
}
}

View File

@ -1,57 +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.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.PostMediaViewHolder;
import awais.instagrabber.databinding.ItemChildPostBinding;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.ViewerPostModel;
public final class PostsMediaAdapter extends RecyclerView.Adapter<PostMediaViewHolder> {
private final View.OnClickListener clickListener;
private ViewerPostModel[] postModels;
public PostsMediaAdapter(final ViewerPostModel[] postModels, final View.OnClickListener clickListener) {
this.postModels = postModels;
this.clickListener = clickListener;
}
@NonNull
@Override
public PostMediaViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
layoutInflater.inflate(R.layout.item_child_post, parent, false);
final ItemChildPostBinding binding = ItemChildPostBinding.inflate(layoutInflater, parent, false);
return new PostMediaViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostMediaViewHolder holder, final int position) {
final ViewerPostModel postModel = postModels[position];
holder.bind(postModel, position, clickListener);
}
public void setData(final ViewerPostModel[] postModels) {
this.postModels = postModels;
notifyDataSetChanged();
}
public ViewerPostModel getItemAt(final int position) {
return postModels == null ? null : postModels[position];
}
@Override
public int getItemCount() {
return postModels == null ? 0 : postModels.length;
}
public BasePostModel[] getPostModels() {
return postModels;
}
}

View File

@ -0,0 +1,60 @@
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.TopicClusterViewHolder;
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
import awais.instagrabber.repositories.responses.saved.SavedCollection;
import awais.instagrabber.utils.ResponseBodyUtils;
public class SavedCollectionsAdapter extends ListAdapter<SavedCollection, TopicClusterViewHolder> {
private static final DiffUtil.ItemCallback<SavedCollection> DIFF_CALLBACK = new DiffUtil.ItemCallback<SavedCollection>() {
@Override
public boolean areItemsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
if (oldItem.getCoverMedias() != null && newItem.getCoverMedias() != null
&& oldItem.getCoverMedias().size() == newItem.getCoverMedias().size()) {
return oldItem.getCoverMedias().get(0).getId().equals(newItem.getCoverMedias().get(0).getId());
}
else if (oldItem.getCoverMedia() != null && newItem.getCoverMedia() != null) {
return oldItem.getCoverMedia().getId().equals(newItem.getCoverMedia().getId());
}
return false;
}
};
private final OnCollectionClickListener onCollectionClickListener;
public SavedCollectionsAdapter(final OnCollectionClickListener onCollectionClickListener) {
super(DIFF_CALLBACK);
this.onCollectionClickListener = onCollectionClickListener;
}
@NonNull
@Override
public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
return new TopicClusterViewHolder(binding, null, onCollectionClickListener);
}
@Override
public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
final SavedCollection topicCluster = getItem(position);
holder.bind(topicCluster);
}
public interface OnCollectionClickListener {
void onCollectionClick(SavedCollection savedCollection, View root, View cover, View title, int titleColor, int backgroundColor);
}
}

View File

@ -12,4 +12,7 @@ public class SliderCallbackAdapter implements SliderItemsAdapter.SliderCallback
@Override @Override
public void onPlayerPause(final int position) {} public void onPlayerPause(final int position) {}
@Override
public void onPlayerRelease(final int position) {}
} }

View File

@ -14,25 +14,25 @@ import awais.instagrabber.customviews.VerticalDragHelper;
import awais.instagrabber.databinding.ItemSliderPhotoBinding; import awais.instagrabber.databinding.ItemSliderPhotoBinding;
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding; import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding; import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Media;
public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemViewHolder> { public final class SliderItemsAdapter extends ListAdapter<Media, SliderItemViewHolder> {
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener; private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
private final boolean loadVideoOnItemClick; private final boolean loadVideoOnItemClick;
private final SliderCallback sliderCallback; private final SliderCallback sliderCallback;
private final LayoutExoCustomControlsBinding controlsBinding; private final LayoutExoCustomControlsBinding controlsBinding;
private static final DiffUtil.ItemCallback<PostChild> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostChild>() { private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {
@Override @Override
public boolean areItemsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) { public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPostId().equals(newItem.getPostId()); return oldItem.getPk().equals(newItem.getPk());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) { public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPostId().equals(newItem.getPostId()); return oldItem.getPk().equals(newItem.getPk());
} }
}; };
@ -66,14 +66,14 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
@Override @Override
public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {
final PostChild model = getItem(position); final Media media = getItem(position);
holder.bind(model, position, sliderCallback); holder.bind(media, position, sliderCallback);
} }
@Override @Override
public int getItemViewType(final int position) { public int getItemViewType(final int position) {
final PostChild viewerPostModel = getItem(position); final Media media = getItem(position);
return viewerPostModel.getItemType().getId(); return media.getMediaType().getId();
} }
// @NonNull // @NonNull
@ -148,5 +148,7 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
void onPlayerPlay(int position); void onPlayerPlay(int position);
void onPlayerPause(int position); void onPlayerPause(int position);
void onPlayerRelease(int position);
} }
} }

View File

@ -0,0 +1,151 @@
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 androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.RecipientThreadViewHolder;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
public final class UserSearchResultsAdapter extends ListAdapter<RankedRecipient, RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_USER = 0;
private static final int VIEW_TYPE_THREAD = 1;
private static final DiffUtil.ItemCallback<RankedRecipient> DIFF_CALLBACK = new DiffUtil.ItemCallback<RankedRecipient>() {
@Override
public boolean areItemsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {
final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;
if (!bothUsers) return false;
final boolean bothThreads = oldItem.getThread() != null && newItem.getThread() != null;
if (!bothThreads) return false;
if (bothUsers) {
return oldItem.getUser().getPk() == newItem.getUser().getPk();
}
return Objects.equals(oldItem.getThread().getThreadId(), newItem.getThread().getThreadId());
}
@Override
public boolean areContentsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {
final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;
if (bothUsers) {
return Objects.equals(oldItem.getUser().getUsername(), newItem.getUser().getUsername()) &&
Objects.equals(oldItem.getUser().getFullName(), newItem.getUser().getFullName());
}
return Objects.equals(oldItem.getThread().getThreadTitle(), newItem.getThread().getThreadTitle());
}
};
private final boolean showSelection;
private final Set<RankedRecipient> selectedRecipients;
private final OnDirectUserClickListener onUserClickListener;
private final OnRecipientClickListener onRecipientClickListener;
public UserSearchResultsAdapter(final boolean showSelection,
final OnRecipientClickListener onRecipientClickListener) {
super(DIFF_CALLBACK);
this.showSelection = showSelection;
selectedRecipients = showSelection ? new HashSet<>() : null;
this.onRecipientClickListener = onRecipientClickListener;
this.onUserClickListener = (position, user, selected) -> {
if (onRecipientClickListener != null) {
onRecipientClickListener.onClick(position, RankedRecipient.of(user), selected);
}
};
setHasStableIds(true);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
if (viewType == VIEW_TYPE_USER) {
return new DirectUserViewHolder(binding, onUserClickListener, null);
}
return new RecipientThreadViewHolder(binding, onRecipientClickListener);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final RankedRecipient recipient = getItem(position);
final int itemViewType = getItemViewType(position);
if (itemViewType == VIEW_TYPE_USER) {
boolean isSelected = false;
if (selectedRecipients != null) {
isSelected = selectedRecipients.stream()
.anyMatch(rankedRecipient -> rankedRecipient.getUser() != null
&& rankedRecipient.getUser().getPk() == recipient.getUser().getPk());
}
((DirectUserViewHolder) holder).bind(position, recipient.getUser(), false, false, showSelection, isSelected);
return;
}
boolean isSelected = false;
if (selectedRecipients != null) {
isSelected = selectedRecipients.stream()
.anyMatch(rankedRecipient -> rankedRecipient.getThread() != null
&& Objects.equals(rankedRecipient.getThread().getThreadId(), recipient.getThread().getThreadId()));
}
((RecipientThreadViewHolder) holder).bind(position, recipient.getThread(), showSelection, isSelected);
}
@Override
public long getItemId(final int position) {
final RankedRecipient recipient = getItem(position);
if (recipient.getUser() != null) {
return recipient.getUser().getPk();
}
if (recipient.getThread() != null) {
return recipient.getThread().getThreadTitle().hashCode();
}
return 0;
}
@Override
public int getItemViewType(final int position) {
final RankedRecipient recipient = getItem(position);
return recipient.getUser() != null ? VIEW_TYPE_USER : VIEW_TYPE_THREAD;
}
public void setSelectedRecipient(final RankedRecipient recipient, final boolean selected) {
if (selectedRecipients == null || recipient == null || (recipient.getUser() == null && recipient.getThread() == null)) return;
final boolean isUser = recipient.getUser() != null;
int position = -1;
final List<RankedRecipient> currentList = getCurrentList();
for (int i = 0; i < currentList.size(); i++) {
final RankedRecipient temp = currentList.get(i);
if (isUser) {
if (temp.getUser() != null && temp.getUser().getPk() == recipient.getUser().getPk()) {
position = i;
break;
}
continue;
}
if (temp.getThread() != null && Objects.equals(temp.getThread().getThreadId(), recipient.getThread().getThreadId())) {
position = i;
break;
}
}
if (position < 0) return;
if (selected) {
selectedRecipients.add(recipient);
} else {
selectedRecipients.remove(recipient);
}
notifyItemChanged(position);
}
public interface OnRecipientClickListener {
void onClick(int position, RankedRecipient recipient, final boolean isSelected);
}
}

View File

@ -1,132 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.content.Context;
import android.net.Uri;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder {
private final LinearLayout multipleProfilePicsContainer;
private final SimpleDraweeView[] multipleProfilePics;
private final LayoutDmInboxItemBinding binding;
public DirectMessageInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
multipleProfilePicsContainer = binding.multiPicContainer;
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1);
multipleProfilePics = new SimpleDraweeView[]{
(SimpleDraweeView) multipleProfilePicsContainer.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(1)
};
binding.tvDate.setSelected(true);
binding.tvUsername.setSelected(true);
}
public void bind(final InboxThreadModel model) {
final DirectItemModel[] itemModels;
if (model == null || (itemModels = model.getItems()) == null) {
return;
}
itemView.setTag(model);
final ProfileModel[] users = model.getUsers();
if (users.length > 1) {
binding.ivProfilePic.setVisibility(View.GONE);
multipleProfilePicsContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.length); ++i) {
multipleProfilePics[i].setImageURI(users[i].getSdProfilePic());
}
} else {
final String uriString = users.length == 1 ? users[0].getSdProfilePic() : null;
if (uriString == null) {
binding.ivProfilePic.setVisibility(View.GONE);
} else {
binding.ivProfilePic.setVisibility(View.VISIBLE);
multipleProfilePicsContainer.setVisibility(View.GONE);
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uriString))
.setResizeOptions(new ResizeOptions(50, 50))
.build();
binding.ivProfilePic.setController(
Fresco.newDraweeControllerBuilder()
.setOldController(binding.ivProfilePic.getController())
.setImageRequest(request)
.build()
);
}
}
binding.tvUsername.setText(model.getThreadTitle());
final int length = itemModels.length;
DirectItemModel lastItemModel = null;
if (length != 0) {
lastItemModel = itemModels[length - 1];
}
if (lastItemModel == null) {
return;
}
final DirectItemType itemType = lastItemModel.getItemType();
// binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE);
final Context context = itemView.getContext();
final CharSequence messageText;
switch (itemType) {
case TEXT:
case LIKE:
messageText = lastItemModel.getText();
break;
case LINK:
messageText = context.getString(R.string.direct_messages_sent_link);
break;
case MEDIA:
case MEDIA_SHARE:
case RAVEN_MEDIA:
case CLIP:
case FELIX_SHARE:
messageText = context.getString(R.string.direct_messages_sent_media);
break;
case ACTION_LOG:
final DirectItemModel.DirectItemActionLogModel logModel = lastItemModel.getActionLogModel();
messageText = logModel != null ? logModel.getDescription() : "...";
break;
case REEL_SHARE:
final DirectItemModel.DirectItemReelShareModel reelShare = lastItemModel.getReelShare();
if (reelShare == null)
messageText = context.getString(R.string.direct_messages_sent_media);
else {
final String reelType = reelShare.getType();
final int textRes;
if ("reply".equals(reelType))
textRes = R.string.direct_messages_replied_story;
else if ("mention".equals(reelType))
textRes = R.string.direct_messages_mention_story;
else if ("reaction".equals(reelType))
textRes = R.string.direct_messages_reacted_story;
else textRes = R.string.direct_messages_sent_media;
messageText = context.getString(textRes) + " : " + reelShare.getText();
}
break;
default:
messageText = "<i>Unsupported message</i>";
}
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT));
binding.tvDate.setText(lastItemModel.getDateTime());
binding.unread.setVisibility(model.getUnreadCount() > 0L ? View.VISIBLE : View.INVISIBLE);
}
}

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder; import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.imagepipeline.request.ImageRequestBuilder;
@ -20,9 +21,10 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.DownloadedCheckerAsyncTask; import awais.instagrabber.asyncs.DownloadedCheckerAsyncTask;
import awais.instagrabber.databinding.ItemFeedGridBinding; import awais.instagrabber.databinding.ItemFeedGridBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID; import static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;
@ -36,7 +38,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
} }
public void bind(final int position, public void bind(final int position,
@NonNull final FeedModel feedModel, @NonNull final Media media,
@NonNull final PostsLayoutPreferences layoutPreferences, @NonNull final PostsLayoutPreferences layoutPreferences,
final FeedAdapterV2.FeedItemCallback feedItemCallback, final FeedAdapterV2.FeedItemCallback feedItemCallback,
final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback, final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
@ -44,28 +46,121 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
final boolean selected) { final boolean selected) {
itemView.setOnClickListener(v -> { itemView.setOnClickListener(v -> {
if (!selectionModeActive && feedItemCallback != null) { if (!selectionModeActive && feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage); feedItemCallback.onPostClick(media, binding.profilePic, binding.postImage);
return; return;
} }
if (selectionModeActive && adapterSelectionCallback != null) { if (selectionModeActive && adapterSelectionCallback != null) {
adapterSelectionCallback.onPostClick(position, feedModel); adapterSelectionCallback.onPostClick(position, media);
} }
}); });
if (adapterSelectionCallback != null) { if (adapterSelectionCallback != null) {
itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, feedModel)); itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, media));
} }
binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE); binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE);
// for rounded borders (clip view to background shape) // for rounded borders (clip view to background shape)
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners()); itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
if (layoutPreferences.getType() == STAGGERED_GRID) { if (layoutPreferences.getType() == STAGGERED_GRID) {
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight(); final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
binding.postImage.setAspectRatio(aspectRatio); binding.postImage.setAspectRatio(aspectRatio);
} else { } else {
binding.postImage.setAspectRatio(1); binding.postImage.setAspectRatio(1);
} }
setUserDetails(media, layoutPreferences);
String thumbnailUrl = null;
final int typeIconRes;
switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE:
typeIconRes = -1;
thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
break;
case MEDIA_TYPE_VIDEO:
thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
typeIconRes = R.drawable.exo_icon_play;
break;
case MEDIA_TYPE_SLIDER:
final List<Media> sliderItems = media.getCarouselMedia();
if (sliderItems != null) {
final Media child = sliderItems.get(0);
if (child != null) {
thumbnailUrl = ResponseBodyUtils.getThumbUrl(child);
if (layoutPreferences.getType() == STAGGERED_GRID) {
final float childAspectRatio = (float) child.getOriginalWidth() / child.getOriginalHeight();
binding.postImage.setAspectRatio(childAspectRatio);
}
}
}
typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;
break;
default:
typeIconRes = -1;
thumbnailUrl = null;
}
setThumbImage(thumbnailUrl);
if (typeIconRes <= 0) {
binding.typeIcon.setVisibility(View.GONE);
} else {
binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(typeIconRes);
}
final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> {
final List<Boolean> checkList = result.get(media.getPk());
if (checkList == null || checkList.isEmpty()) {
return;
}
switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE:
case MEDIA_TYPE_VIDEO:
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(R.color.green_A400)));
break;
case MEDIA_TYPE_SLIDER:
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
final List<Media> carouselMedia = media.getCarouselMedia();
boolean allDownloaded = checkList.size() == (carouselMedia == null ? 0 : carouselMedia.size());
if (allDownloaded) {
allDownloaded = checkList.stream().allMatch(downloaded -> downloaded);
}
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(
allDownloaded ? R.color.green_A400 : R.color.yellow_400)));
break;
default:
}
});
task.execute(media);
}
private void setThumbImage(final String thumbnailUrl) {
if (TextUtils.isEmpty(thumbnailUrl)) {
binding.postImage.setController(null);
return;
}
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
.setResizeOptions(ResizeOptions.forDimensions(binding.postImage.getWidth(),
binding.postImage.getHeight()))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.postImage.getController());
binding.postImage.setController(builder.build());
}
private void setUserDetails(@NonNull final Media media,
@NonNull final PostsLayoutPreferences layoutPreferences) {
final User user = media.getUser();
if (layoutPreferences.isAvatarVisible()) { if (layoutPreferences.isAvatarVisible()) {
binding.profilePic.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getSdProfilePic()) ? View.GONE : View.VISIBLE); if (user == null) {
binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic()); binding.profilePic.setVisibility(View.GONE);
} else {
final String profilePicUrl = user.getProfilePicUrl();
if (TextUtils.isEmpty(profilePicUrl)) {
binding.profilePic.setVisibility(View.GONE);
} else {
binding.profilePic.setVisibility(View.VISIBLE);
binding.profilePic.setImageURI(profilePicUrl);
}
}
final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();
@DimenRes final int dimenRes; @DimenRes final int dimenRes;
switch (layoutPreferences.getProfilePicSize()) { switch (layoutPreferences.getProfilePicSize()) {
@ -88,74 +183,19 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.profilePic.setVisibility(View.GONE); binding.profilePic.setVisibility(View.GONE);
} }
if (layoutPreferences.isNameVisible()) { if (layoutPreferences.isNameVisible()) {
binding.name.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getUsername()) ? View.GONE : View.VISIBLE); if (user == null) {
binding.name.setText(feedModel.getProfileModel().getUsername()); binding.name.setVisibility(View.GONE);
} else {
final String username = user.getUsername();
if (username == null) {
binding.name.setVisibility(View.GONE);
} else {
binding.name.setVisibility(View.VISIBLE);
binding.name.setText(username);
}
}
} else { } else {
binding.name.setVisibility(View.GONE); binding.name.setVisibility(View.GONE);
} }
String thumbnailUrl = null;
final int typeIconRes;
switch (feedModel.getItemType()) {
case MEDIA_TYPE_IMAGE:
typeIconRes = -1;
thumbnailUrl = feedModel.getThumbnailUrl();
break;
case MEDIA_TYPE_VIDEO:
thumbnailUrl = feedModel.getThumbnailUrl();
typeIconRes = R.drawable.exo_icon_play;
break;
case MEDIA_TYPE_SLIDER:
final List<PostChild> sliderItems = feedModel.getSliderItems();
if (sliderItems != null) {
thumbnailUrl = sliderItems.get(0).getThumbnailUrl();
}
typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;
break;
default:
typeIconRes = -1;
thumbnailUrl = null;
}
if (TextUtils.isEmpty(thumbnailUrl)) {
binding.postImage.setController(null);
return;
}
if (typeIconRes <= 0) {
binding.typeIcon.setVisibility(View.GONE);
} else {
binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(typeIconRes);
}
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.postImage.getController());
binding.postImage.setController(builder.build());
final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> {
final List<Boolean> checkList = result.get(feedModel.getPostId());
if (checkList == null || checkList.isEmpty()) {
return;
}
switch (feedModel.getItemType()) {
case MEDIA_TYPE_IMAGE:
case MEDIA_TYPE_VIDEO:
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(R.color.green_A400)));
break;
case MEDIA_TYPE_SLIDER:
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
boolean allDownloaded = checkList.size() == feedModel.getSliderItems().size();
if (allDownloaded) {
allDownloaded = checkList.stream().allMatch(downloaded -> downloaded);
}
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(
allDownloaded ? R.color.green_A400 : R.color.yellow_400)));
break;
default:
}
});
task.execute(feedModel);
} }
} }

View File

@ -5,7 +5,7 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedStoriesAdapter; import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.databinding.ItemHighlightBinding; import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.User;
public final class FeedStoryViewHolder extends RecyclerView.ViewHolder { public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
@ -28,10 +28,10 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
if (listener != null) listener.onFeedStoryLongClick(model, position); if (listener != null) listener.onFeedStoryLongClick(model, position);
return true; return true;
}); });
final ProfileModel profileModel = model.getProfileModel(); final User profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername()); binding.title.setText(profileModel.getUsername());
binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getSdProfilePic()); binding.icon.setImageURI(profileModel.getProfilePicUrl());
binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
if (model.isLive()) binding.icon.setStoriesBorder(2); if (model.isLive()) binding.icon.setStoriesBorder(2);

View File

@ -0,0 +1,72 @@
package awais.instagrabber.adapters.viewholder;
import android.graphics.Bitmap;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import awais.instagrabber.adapters.FiltersAdapter;
import awais.instagrabber.databinding.ItemFilterBinding;
import awais.instagrabber.fragments.imageedit.filters.filters.Filter;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.BitmapUtils;
import jp.co.cyberagent.android.gpuimage.GPUImage;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
public class FilterViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = FilterViewHolder.class.getSimpleName();
private final ItemFilterBinding binding;
private final Collection<GPUImageFilter> tuneFilters;
private final FiltersAdapter.OnFilterClickListener onFilterClickListener;
private final AppExecutors appExecutors;
public FilterViewHolder(@NonNull final ItemFilterBinding binding,
final Collection<GPUImageFilter> tuneFilters,
final FiltersAdapter.OnFilterClickListener onFilterClickListener) {
super(binding.getRoot());
this.binding = binding;
this.tuneFilters = tuneFilters;
this.onFilterClickListener = onFilterClickListener;
appExecutors = AppExecutors.getInstance();
}
public void bind(final int position, final String originalKey, final Bitmap originalBitmap, final Filter<?> item, final boolean isSelected) {
if (originalBitmap == null || item == null) return;
if (onFilterClickListener != null) {
itemView.setOnClickListener(v -> onFilterClickListener.onClick(position, item));
}
if (item.getLabel() != -1) {
binding.name.setVisibility(View.VISIBLE);
binding.name.setText(item.getLabel());
binding.name.setSelected(isSelected);
} else {
binding.name.setVisibility(View.GONE);
}
final String filterKey = item.getLabel() + "_" + originalKey;
// avoid resetting the bitmap
if (binding.preview.getTag() != null && binding.preview.getTag().equals(filterKey)) return;
binding.preview.setTag(filterKey);
final Bitmap bitmap = BitmapUtils.getBitmapFromMemCache(filterKey);
if (bitmap == null) {
final GPUImageFilter filter = item.getInstance();
appExecutors.tasksThread().submit(() -> {
GPUImage.getBitmapForMultipleFilters(
originalBitmap,
ImmutableList.<GPUImageFilter>builder().add(filter).addAll(tuneFilters).build(),
filteredBitmap -> {
BitmapUtils.addBitmapToMemoryCache(filterKey, filteredBitmap, true);
appExecutors.mainThread().execute(() -> binding.getRoot().post(() -> binding.preview.setImageBitmap(filteredBitmap)));
}
);
});
return;
}
binding.getRoot().post(() -> binding.preview.setImageBitmap(bitmap));
}
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters.viewholder; package awais.instagrabber.adapters.viewholder;
import android.util.Log;
import android.view.View; import android.view.View;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -9,7 +8,7 @@ import java.util.List;
import awais.instagrabber.databinding.ItemFollowBinding; import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.FollowModel; import awais.instagrabber.models.FollowModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.User;
public final class FollowsViewHolder extends RecyclerView.ViewHolder { public final class FollowsViewHolder extends RecyclerView.ViewHolder {
@ -20,18 +19,18 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
this.binding = binding; this.binding = binding;
} }
public void bind(final ProfileModel model, public void bind(final User model,
final List<Long> admins, final List<Long> admins,
final View.OnClickListener onClickListener) { final View.OnClickListener onClickListener) {
if (model == null) return; if (model == null) return;
itemView.setTag(model); itemView.setTag(model);
itemView.setOnClickListener(onClickListener); itemView.setOnClickListener(onClickListener);
binding.tvUsername.setText(model.getUsername()); binding.tvUsername.setText(model.getUsername());
binding.tvFullName.setText(model.getName()); binding.tvFullName.setText(model.getFullName());
if (admins != null && admins.contains(Long.parseLong(model.getId()))) { if (admins != null && admins.contains(model.getPk())) {
binding.isAdmin.setVisibility(View.VISIBLE); binding.isAdmin.setVisibility(View.VISIBLE);
} }
binding.ivProfilePic.setImageURI(model.getSdProfilePic()); binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
} }
public void bind(final FollowModel model, public void bind(final FollowModel model,

View File

@ -1,17 +1,16 @@
package awais.instagrabber.adapters.viewholder; package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs;
public final class NotificationViewHolder extends RecyclerView.ViewHolder { public final class NotificationViewHolder extends RecyclerView.ViewHolder {
private final ItemNotificationBinding binding; private final ItemNotificationBinding binding;
@ -21,22 +20,19 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
this.binding = binding; this.binding = binding;
} }
public void bind(final NotificationModel model, public void bind(final Notification model,
final OnNotificationClickListener notificationClickListener) { final OnNotificationClickListener notificationClickListener) {
if (model == null) return; if (model == null) return;
int text = -1; int text = -1;
CharSequence subtext = null; CharSequence subtext = null;
final NotificationArgs args = model.getArgs();
switch (model.getType()) { switch (model.getType()) {
case LIKE: case LIKE:
text = R.string.liked_notif; text = R.string.liked_notif;
break; break;
case COMMENT: case COMMENT: // untested
text = R.string.comment_notif; text = R.string.comment_notif;
subtext = model.getText(); subtext = args.getText();
break;
case MENTION:
text = R.string.mention_notif;
subtext = model.getText();
break; break;
case TAGGED: case TAGGED:
text = R.string.tagged_notif; text = R.string.tagged_notif;
@ -46,47 +42,50 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
break; break;
case REQUEST: case REQUEST:
text = R.string.request_notif; text = R.string.request_notif;
subtext = model.getText();
break; break;
case COMMENT_MENTION:
case COMMENT_LIKE: case COMMENT_LIKE:
case TAGGED_COMMENT: case TAGGED_COMMENT:
case RESPONDED_STORY: case RESPONDED_STORY:
subtext = model.getText(); subtext = args.getText();
break; break;
case AYML: case AYML:
subtext = model.getPostId(); subtext = args.getFullName();
break; break;
} }
binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext); binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? args.getText() : subtext);
if (text == -1 && subtext != null) { if (text == -1 && subtext != null) {
binding.tvComment.setText(subtext); binding.tvComment.setText(args.getText());
binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE); binding.tvComment.setVisibility(TextUtils.isEmpty(args.getText()) || args.getText().equals(args.getFullName())
? View.GONE : View.VISIBLE);
binding.tvSubComment.setText(subtext);
binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE); binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);
} } else if (text != -1) {
else if (text != -1) {
binding.tvComment.setText(text); binding.tvComment.setText(text);
binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE); binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);
} }
if (model.getType() != NotificationType.REQUEST && model.getType() != NotificationType.AYML) { binding.tvDate.setVisibility(model.getType() == NotificationType.AYML ? View.GONE : View.VISIBLE);
binding.tvDate.setText(model.getDateTime()); if (model.getType() != NotificationType.AYML) {
binding.tvDate.setText(args.getDateTime());
} }
binding.tvUsername.setText(model.getUsername()); binding.isVerified.setVisibility(args.isVerified() ? View.VISIBLE : View.GONE);
binding.ivProfilePic.setImageURI(model.getProfilePic());
binding.tvUsername.setText(args.getUsername());
binding.ivProfilePic.setImageURI(args.getProfilePic());
binding.ivProfilePic.setOnClickListener(v -> { binding.ivProfilePic.setOnClickListener(v -> {
if (notificationClickListener == null) return; if (notificationClickListener == null) return;
notificationClickListener.onProfileClick(model.getUsername()); notificationClickListener.onProfileClick(args.getUsername());
}); });
if (model.getType() == NotificationType.AYML) { if (model.getType() == NotificationType.AYML) {
binding.ivPreviewPic.setVisibility(View.GONE); binding.ivPreviewPic.setVisibility(View.GONE);
} } else if (args.getMedia() == null) {
else if (TextUtils.isEmpty(model.getPreviewPic())) {
binding.ivPreviewPic.setVisibility(View.INVISIBLE); binding.ivPreviewPic.setVisibility(View.INVISIBLE);
} else { } else {
binding.ivPreviewPic.setVisibility(View.VISIBLE); binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getPreviewPic()); binding.ivPreviewPic.setImageURI(args.getMedia().get(0).getImage());
binding.ivPreviewPic.setOnClickListener(v -> { binding.ivPreviewPic.setOnClickListener(v -> {
if (notificationClickListener == null) return; if (notificationClickListener == null) return;
notificationClickListener.onPreviewClick(model); notificationClickListener.onPreviewClick(model);

View File

@ -1,29 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.databinding.ItemChildPostBinding;
import awais.instagrabber.models.ViewerPostModel;
public final class PostMediaViewHolder extends RecyclerView.ViewHolder {
private final ItemChildPostBinding binding;
public PostMediaViewHolder(@NonNull final ItemChildPostBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
if (model == null) return;
// model.setPosition(position);
itemView.setTag(model);
itemView.setOnClickListener(clickListener);
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
binding.isDownloaded.setVisibility(model.isDownloaded() ? View.VISIBLE : View.GONE);
binding.icon.setImageURI(model.getDisplayUrl());
}
}

View File

@ -1,43 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemClickListener;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemLongClickListener;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class PostViewHolder extends RecyclerView.ViewHolder {
private final ItemPostBinding binding;
public PostViewHolder(@NonNull final ItemPostBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final PostModel postModel,
final int position,
final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
if (postModel == null) return;
// postModel.setPosition(position);
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
final MediaItemType itemType = postModel.getItemType();
final boolean isSlider = itemType == MediaItemType.MEDIA_TYPE_SLIDER;
binding.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
binding.typeIcon.setVisibility(itemType == MediaItemType.MEDIA_TYPE_VIDEO || isSlider ? View.VISIBLE : View.GONE);
binding.typeIcon.setImageResource(isSlider ? R.drawable.ic_slider_24 : R.drawable.ic_video_24);
binding.selectedView.setVisibility(postModel.isSelected() ? View.VISIBLE : View.GONE);
binding.postImage.setImageURI(postModel.getThumbnailUrl());
}
}

View File

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.SliderItemsAdapter; import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.models.PostChild; import awais.instagrabber.repositories.responses.Media;
public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder { public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = "FeedSliderItemViewHolder"; private static final String TAG = "FeedSliderItemViewHolder";
@ -15,7 +15,7 @@ public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
super(itemView); super(itemView);
} }
public abstract void bind(final PostChild model, public abstract void bind(final Media media,
final int position, final int position,
final SliderItemsAdapter.SliderCallback sliderCallback); final SliderItemsAdapter.SliderCallback sliderCallback);
} }

View File

@ -2,6 +2,8 @@ package awais.instagrabber.adapters.viewholder;
import android.graphics.drawable.Animatable; import android.graphics.drawable.Animatable;
import android.net.Uri; import android.net.Uri;
import android.view.GestureDetector;
import android.view.MotionEvent;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -15,7 +17,8 @@ import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.customviews.VerticalDragHelper; import awais.instagrabber.customviews.VerticalDragHelper;
import awais.instagrabber.customviews.drawee.AnimatedZoomableController; import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
import awais.instagrabber.databinding.ItemSliderPhotoBinding; import awais.instagrabber.databinding.ItemSliderPhotoBinding;
import awais.instagrabber.models.PostChild; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.ResponseBodyUtils;
public class SliderPhotoViewHolder extends SliderItemViewHolder { public class SliderPhotoViewHolder extends SliderItemViewHolder {
private static final String TAG = "FeedSliderPhotoViewHolder"; private static final String TAG = "FeedSliderPhotoViewHolder";
@ -30,11 +33,11 @@ public class SliderPhotoViewHolder extends SliderItemViewHolder {
this.onVerticalDragListener = onVerticalDragListener; this.onVerticalDragListener = onVerticalDragListener;
} }
public void bind(@NonNull final PostChild model, public void bind(@NonNull final Media model,
final int position, final int position,
final SliderItemsAdapter.SliderCallback sliderCallback) { final SliderItemsAdapter.SliderCallback sliderCallback) {
final ImageRequest requestBuilder = ImageRequestBuilder final ImageRequest requestBuilder = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(model.getDisplayUrl())) .newBuilderWithSource(Uri.parse(ResponseBodyUtils.getImageUrl(model)))
.setLocalThumbnailPreviewsEnabled(true) .setLocalThumbnailPreviewsEnabled(true)
.build(); .build();
binding.getRoot() binding.getRoot()
@ -57,11 +60,21 @@ public class SliderPhotoViewHolder extends SliderItemViewHolder {
} }
} }
}) })
.setLowResImageRequest(ImageRequest.fromUri(model.getThumbnailUrl())) .setLowResImageRequest(ImageRequest.fromUri(ResponseBodyUtils.getThumbUrl(model)))
.build()); .build());
binding.getRoot().setOnClickListener(v -> { // binding.getRoot().setOnClickListener(v -> {
if (sliderCallback != null) { // if (sliderCallback != null) {
sliderCallback.onItemClicked(position); // sliderCallback.onItemClicked(position);
// }
// });
binding.getRoot().setTapListener(new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(final MotionEvent e) {
if (sliderCallback != null) {
sliderCallback.onItemClicked(position);
return true;
}
return false;
} }
}); });
final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance(); final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance();

View File

@ -1,19 +1,25 @@
package awais.instagrabber.adapters.viewholder; package awais.instagrabber.adapters.viewholder;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.util.List;
import awais.instagrabber.adapters.SliderItemsAdapter; import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.customviews.VerticalDragHelper; import awais.instagrabber.customviews.VerticalDragHelper;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter; import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper; import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding; import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding; import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
import awais.instagrabber.models.PostChild; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -24,6 +30,14 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
private final LayoutVideoPlayerWithThumbnailBinding binding; private final LayoutVideoPlayerWithThumbnailBinding binding;
private final LayoutExoCustomControlsBinding controlsBinding; private final LayoutExoCustomControlsBinding controlsBinding;
private final boolean loadVideoOnItemClick; private final boolean loadVideoOnItemClick;
private final GestureDetector.OnGestureListener videoPlayerViewGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
binding.playerView.performClick();
return true;
}
};
private VideoPlayerViewHelper videoPlayerViewHelper; private VideoPlayerViewHelper videoPlayerViewHelper;
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@ -35,29 +49,27 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
this.binding = binding; this.binding = binding;
this.controlsBinding = controlsBinding; this.controlsBinding = controlsBinding;
this.loadVideoOnItemClick = loadVideoOnItemClick; this.loadVideoOnItemClick = loadVideoOnItemClick;
if (onVerticalDragListener != null) { // if (onVerticalDragListener != null) {
final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent); // final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView); // final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener); // thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener); // playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
binding.thumbnailParent.setOnTouchListener((v, event) -> { // binding.thumbnailParent.setOnTouchListener((v, event) -> {
final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event); // final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
if (onDragTouch) { // if (onDragTouch) {
return true; // return true;
} // }
return thumbnailVerticalDragHelper.onGestureTouchEvent(event); // return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
}); // });
binding.playerView.setOnTouchListener((v, event) -> { // }
final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event); final GestureDetector gestureDetector = new GestureDetector(itemView.getContext(), videoPlayerViewGestureListener);
if (onDragTouch) { binding.playerView.setOnTouchListener((v, event) -> {
return true; gestureDetector.onTouchEvent(event);
} return true;
return playerVerticalDragHelper.onGestureTouchEvent(event); });
});
}
} }
public void bind(@NonNull final PostChild model, public void bind(@NonNull final Media media,
final int position, final int position,
final SliderItemsAdapter.SliderCallback sliderCallback) { final SliderItemsAdapter.SliderCallback sliderCallback) {
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f; final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
@ -82,7 +94,7 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE); // binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels; final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth()); final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
layoutParams.width = requiredWidth; layoutParams.width = requiredWidth;
layoutParams.height = resultingHeight; layoutParams.height = resultingHeight;
binding.playerView.requestLayout(); binding.playerView.requestLayout();
@ -102,14 +114,30 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
sliderCallback.onPlayerPause(position); sliderCallback.onPlayerPause(position);
} }
} }
@Override
public void onRelease() {
if (sliderCallback != null) {
sliderCallback.onPlayerRelease(position);
}
}
}; };
final float aspectRatio = (float) model.getWidth() / model.getHeight(); final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
String videoUrl = null;
final List<VideoVersion> videoVersions = media.getVideoVersions();
if (videoVersions != null && !videoVersions.isEmpty()) {
final VideoVersion videoVersion = videoVersions.get(0);
if (videoVersion != null) {
videoUrl = videoVersion.getUrl();
}
}
if (videoUrl == null) return;
videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(), videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
binding, binding,
model.getDisplayUrl(), videoUrl,
vol, vol,
aspectRatio, aspectRatio,
model.getThumbnailUrl(), ResponseBodyUtils.getThumbUrl(media),
loadVideoOnItemClick, loadVideoOnItemClick,
controlsBinding, controlsBinding,
videoPlayerCallback); videoPlayerCallback);

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters.viewholder; package awais.instagrabber.adapters.viewholder;
import android.text.TextUtils;
import android.view.View; import android.view.View;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -34,7 +33,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
binding.tvDate.setText(model.getDateTime()); binding.tvDate.setText(model.getDateTime());
binding.tvUsername.setText(model.getProfileModel().getUsername()); binding.tvUsername.setText(model.getProfileModel().getUsername());
binding.ivProfilePic.setImageURI(model.getProfileModel().getSdProfilePic()); binding.ivProfilePic.setImageURI(model.getProfileModel().getProfilePicUrl());
binding.ivProfilePic.setOnClickListener(v -> { binding.ivProfilePic.setOnClickListener(v -> {
if (notificationClickListener == null) return; if (notificationClickListener == null) return;
notificationClickListener.onProfileClick(model.getProfileModel().getUsername()); notificationClickListener.onProfileClick(model.getProfileModel().getUsername());
@ -43,8 +42,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
if (model.getFirstStoryModel() != null) { if (model.getFirstStoryModel() != null) {
binding.ivPreviewPic.setVisibility(View.VISIBLE); binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail()); binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail());
} } else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
float alpha = model.isFullyRead() ? 0.5F : 1.0F; float alpha = model.isFullyRead() ? 0.5F : 1.0F;
binding.ivProfilePic.setAlpha(alpha); binding.ivProfilePic.setAlpha(alpha);

View File

@ -25,18 +25,24 @@ import java.util.concurrent.atomic.AtomicInteger;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.DiscoverTopicsAdapter; import awais.instagrabber.adapters.DiscoverTopicsAdapter;
import awais.instagrabber.adapters.SavedCollectionsAdapter;
import awais.instagrabber.databinding.ItemDiscoverTopicBinding; import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
import awais.instagrabber.models.TopicCluster; import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.repositories.responses.saved.SavedCollection;
import awais.instagrabber.utils.ResponseBodyUtils;
public class TopicClusterViewHolder extends RecyclerView.ViewHolder { public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
private final ItemDiscoverTopicBinding binding; private final ItemDiscoverTopicBinding binding;
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener; private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
private final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener;
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding, public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) { final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener,
final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
this.onTopicClickListener = onTopicClickListener; this.onTopicClickListener = onTopicClickListener;
this.onCollectionClickListener = onCollectionClickListener;
} }
public void bind(final TopicCluster topicCluster) { public void bind(final TopicCluster topicCluster) {
@ -57,43 +63,115 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
} }
// binding.title.setTransitionName("title-" + topicCluster.getId()); // binding.title.setTransitionName("title-" + topicCluster.getId());
binding.cover.setTransitionName("cover-" + topicCluster.getId()); binding.cover.setTransitionName("cover-" + topicCluster.getId());
final ImageRequest imageRequest = ImageRequestBuilder final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedia());
.newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl())) if (thumbUrl == null) {
.build(); binding.cover.setImageURI((String) null);
final ImagePipeline imagePipeline = Fresco.getImagePipeline(); } else {
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline final ImageRequest imageRequest = ImageRequestBuilder
.fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance()); .newBuilderWithSource(Uri.parse(thumbUrl))
dataSource.subscribe(new BaseBitmapDataSubscriber() { .build();
@Override final ImagePipeline imagePipeline = Fresco.getImagePipeline();
public void onNewResultImpl(@Nullable Bitmap bitmap) { final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
if (dataSource.isFinished()) { .fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (dataSource.isFinished()) {
dataSource.close();
}
if (bitmap != null) {
Palette.from(bitmap).generate(p -> {
final Resources resources = itemView.getResources();
int titleTextColor = resources.getColor(R.color.white);
if (p != null) {
final Palette.Swatch swatch = p.getDominantSwatch();
if (swatch != null) {
backgroundColor.set(swatch.getRgb());
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.TRANSPARENT, backgroundColor.get()});
titleTextColor = swatch.getTitleTextColor();
binding.background.setBackground(gd);
}
}
titleColor.set(titleTextColor);
binding.title.setTextColor(titleTextColor);
});
}
}
@Override
public void onFailureImpl(@NonNull DataSource dataSource) {
dataSource.close(); dataSource.close();
} }
if (bitmap != null) { }, CallerThreadExecutor.getInstance());
Palette.from(bitmap).generate(p -> { binding.cover.setImageRequest(imageRequest);
final Palette.Swatch swatch = p.getDominantSwatch(); }
final Resources resources = itemView.getResources(); binding.title.setText(topicCluster.getTitle());
int titleTextColor = resources.getColor(R.color.white); }
if (swatch != null) {
backgroundColor.set(swatch.getRgb());
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.TRANSPARENT, backgroundColor.get()});
titleTextColor = swatch.getTitleTextColor();
binding.background.setBackground(gd);
}
titleColor.set(titleTextColor);
binding.title.setTextColor(titleTextColor);
});
}
}
@Override public void bind(final SavedCollection topicCluster) {
public void onFailureImpl(@NonNull DataSource dataSource) { if (topicCluster == null) {
dataSource.close(); return;
} }
}, CallerThreadExecutor.getInstance()); final AtomicInteger titleColor = new AtomicInteger(-1);
binding.cover.setImageRequest(imageRequest); final AtomicInteger backgroundColor = new AtomicInteger(-1);
if (onCollectionClickListener != null) {
itemView.setOnClickListener(v -> onCollectionClickListener.onCollectionClick(
topicCluster,
binding.getRoot(),
binding.cover,
binding.title,
titleColor.get(),
backgroundColor.get()
));
}
// binding.title.setTransitionName("title-" + topicCluster.getId());
binding.cover.setTransitionName("cover-" + topicCluster.getId());
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedias() == null
? topicCluster.getCoverMedia()
: topicCluster.getCoverMedias().get(0));
if (thumbUrl == null) {
binding.cover.setImageURI((String) null);
} else {
final ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(thumbUrl))
.build();
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
.fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (dataSource.isFinished()) {
dataSource.close();
}
if (bitmap != null) {
Palette.from(bitmap).generate(p -> {
final Palette.Swatch swatch = p.getDominantSwatch();
final Resources resources = itemView.getResources();
int titleTextColor = resources.getColor(R.color.white);
if (swatch != null) {
backgroundColor.set(swatch.getRgb());
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.TRANSPARENT, backgroundColor.get()});
titleTextColor = swatch.getTitleTextColor();
binding.background.setBackground(gd);
}
titleColor.set(titleTextColor);
binding.title.setTextColor(titleTextColor);
});
}
}
@Override
public void onFailureImpl(@NonNull DataSource dataSource) {
dataSource.close();
}
}, CallerThreadExecutor.getInstance());
binding.cover.setImageRequest(imageRequest);
}
binding.title.setText(topicCluster.getTitle()); binding.title.setText(topicCluster.getTitle());
} }
} }

View File

@ -9,7 +9,7 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback; import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
import awais.instagrabber.databinding.ItemCommentSmallBinding; import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel; import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public final class ChildCommentViewHolder extends RecyclerView.ViewHolder { public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
} }
private void setUser(final CommentModel comment) { private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel(); final User profileModel = comment.getProfileModel();
if (profileModel == null) return; if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername()); binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic()); binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
} }

View File

@ -9,7 +9,7 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback; import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
import awais.instagrabber.databinding.ItemCommentBinding; import awais.instagrabber.databinding.ItemCommentBinding;
import awais.instagrabber.models.CommentModel; import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public final class ParentCommentViewHolder extends RecyclerView.ViewHolder { public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
} }
private void setUser(final CommentModel comment) { private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel(); final User profileModel = comment.getProfileModel();
if (profileModel == null) return; if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername()); binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic()); binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
} }

View File

@ -0,0 +1,51 @@
package awais.instagrabber.adapters.viewholder.dialogs;
import android.content.Context;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.HashSet;
import awais.instagrabber.R;
import awais.instagrabber.adapters.KeywordsFilterAdapter;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.SettingsHelper;
public class KeywordsFilterDialogViewHolder extends RecyclerView.ViewHolder {
private final Button deleteButton;
private final TextView item;
public KeywordsFilterDialogViewHolder(@NonNull View itemView) {
super(itemView);
deleteButton = itemView.findViewById(R.id.keyword_delete);
item = itemView.findViewById(R.id.keyword_text);
}
public void bind(ArrayList<String> items, int position, Context context, KeywordsFilterAdapter adapter){
item.setText(items.get(position));
deleteButton.setOnClickListener(view -> {
final String s = items.get(position);
SettingsHelper settingsHelper = new SettingsHelper(context);
items.remove(position);
settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items));
adapter.notifyDataSetChanged();
final String message = context.getString(R.string.removed_keywords, s);
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
});
}
public Button getDeleteButton(){
return deleteButton;
}
public TextView getTextView(){
return item;
}
}

View File

@ -0,0 +1,149 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
import awais.instagrabber.utils.DMUtils;
import awais.instagrabber.utils.TextUtils;
public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
// private static final String TAG = "DMInboxItemVH";
private final LayoutDmInboxItemBinding binding;
private final OnItemClickListener onClickListener;
private final List<SimpleDraweeView> multipleProfilePics;
private final int childSmallSize;
private final int childTinySize;
public DirectInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding,
final OnItemClickListener onClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onClickListener = onClickListener;
multipleProfilePics = ImmutableList.of(
binding.multiPic1,
binding.multiPic2,
binding.multiPic3
);
childSmallSize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_small);
childTinySize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_tiny);
}
public void bind(final DirectThread thread) {
if (thread == null) return;
if (onClickListener != null) {
itemView.setOnClickListener((v) -> onClickListener.onItemClick(thread));
}
setProfilePics(thread);
setTitle(thread);
final List<DirectItem> items = thread.getItems();
if (items == null || items.isEmpty()) return;
final DirectItem item = thread.getFirstDirectItem();
if (item == null) return;
setDateTime(item);
setSubtitle(thread);
setReadState(thread);
}
private void setProfilePics(@NonNull final DirectThread thread) {
final List<User> users = thread.getUsers();
if (users.size() > 1) {
binding.profilePic.setVisibility(View.GONE);
binding.multiPicContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.size()); ++i) {
final User user = users.get(i);
final SimpleDraweeView view = multipleProfilePics.get(i);
view.setVisibility(user == null ? View.GONE : View.VISIBLE);
if (user == null) return;
final String profilePicUrl = user.getProfilePicUrl();
view.setImageURI(profilePicUrl);
setChildSize(view, users.size());
if (i == 1) {
updateConstraints(view, users.size());
}
view.requestLayout();
}
return;
}
binding.profilePic.setVisibility(View.VISIBLE);
binding.multiPicContainer.setVisibility(View.GONE);
final String profilePicUrl = users.size() == 1 ? users.get(0).getProfilePicUrl() : null;
if (profilePicUrl == null) {
binding.profilePic.setController(null);
return;
}
binding.profilePic.setImageURI(profilePicUrl);
}
private void updateConstraints(final SimpleDraweeView view, final int length) {
final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) view.getLayoutParams();
if (length >= 2) {
layoutParams.endToEnd = ConstraintSet.PARENT_ID;
layoutParams.bottomToBottom = ConstraintSet.PARENT_ID;
}
if (length == 3) {
layoutParams.startToStart = ConstraintSet.PARENT_ID;
layoutParams.topToTop = ConstraintSet.PARENT_ID;
}
}
private void setChildSize(final SimpleDraweeView view, final int length) {
final int size = length == 3 ? childTinySize : childSmallSize;
final ConstraintLayout.LayoutParams viewLayoutParams = new ConstraintLayout.LayoutParams(size, size);
view.setLayoutParams(viewLayoutParams);
}
private void setTitle(@NonNull final DirectThread thread) {
final String threadTitle = thread.getThreadTitle();
binding.threadTitle.setText(threadTitle);
}
private void setSubtitle(@NonNull final DirectThread thread) {
final Resources resources = itemView.getResources();
final long viewerId = thread.getViewerId();
// final DirectThreadDirectStory directStory = thread.getDirectStory();
// if (directStory != null && !directStory.getItems().isEmpty()) {
// final DirectItem item = directStory.getItems().get(0);
// final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();
// final String username = DMUtils.getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);
// final String subtitle = DMUtils.getMediaSpecificSubtitle(username, resources, mediaType);
// binding.subtitle.setText(subtitle);
// return;
// }
final DirectItem item = thread.getFirstDirectItem();
if (item == null) return;
final String subtitle = DMUtils.getMessageString(thread, resources, viewerId, item);
binding.subtitle.setText(subtitle != null ? subtitle : "");
}
private void setDateTime(@NonNull final DirectItem item) {
final long timestamp = item.getTimestamp() / 1000;
final String dateTimeString = TextUtils.getRelativeDateTimeString(itemView.getContext(), timestamp);
binding.tvDate.setText(dateTimeString);
}
private void setReadState(@NonNull final DirectThread thread) {
final boolean read = DMUtils.isRead(thread);
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
binding.threadTitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
binding.subtitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
}
}

View File

@ -0,0 +1,100 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemActionLog;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemActionLogViewHolder extends DirectItemViewHolder {
private static final String TAG = DirectItemActionLogViewHolder.class.getSimpleName();
private final LayoutDmActionLogBinding binding;
public DirectItemActionLogViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmActionLogBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
binding.tvMessage.setMovementMethod(LinkMovementMethod.getInstance());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemActionLog actionLog = directItemModel.getActionLog();
final String text = actionLog.getDescription();
final SpannableStringBuilder sb = new SpannableStringBuilder(text);
final List<DirectItemActionLog.TextRange> bold = actionLog.getBold();
if (bold != null && !bold.isEmpty()) {
for (final DirectItemActionLog.TextRange textRange : bold) {
final StyleSpan boldStyleSpan = new StyleSpan(Typeface.BOLD);
sb.setSpan(boldStyleSpan, textRange.getStart(), textRange.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
final List<DirectItemActionLog.TextRange> textAttributes = actionLog.getTextAttributes();
if (textAttributes != null && !textAttributes.isEmpty()) {
for (final DirectItemActionLog.TextRange textAttribute : textAttributes) {
if (!TextUtils.isEmpty(textAttribute.getColor())) {
final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));
sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
if (!TextUtils.isEmpty(textAttribute.getIntent())) {
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull final View widget) {
handleDeepLink(textAttribute.getIntent());
}
};
sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
binding.tvMessage.setText(sb);
}
@Override
protected boolean allowMessageDirectionGravity() {
return false;
}
@Override
protected boolean showUserDetailsInGroup() {
return false;
}
@Override
protected boolean showMessageInfo() {
return false;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,68 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.backends.pipeline.Fresco;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;
import awais.instagrabber.repositories.responses.AnimatedMediaImages;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmAnimatedMediaBinding binding;
public DirectItemAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmAnimatedMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemAnimatedMedia animatedMediaModel = item.getAnimatedMedia();
final AnimatedMediaImages images = animatedMediaModel.getImages();
if (images == null) return;
final AnimatedMediaFixedHeight fixedHeight = images.getFixedHeight();
if (fixedHeight == null) return;
final String url = fixedHeight.getWebp();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
fixedHeight.getHeight(),
fixedHeight.getWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();
final int width = widthHeight.first != null ? widthHeight.first : 0;
final int height = widthHeight.second != null ? widthHeight.second : 0;
layoutParams.width = width;
layoutParams.height = height;
binding.ivAnimatedMessage.requestLayout();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(url)
.setAutoPlayAnimations(true)
.build());
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,44 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemDefaultViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemDefaultViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final Context context = itemView.getContext();
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,36 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLikeBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemLikeViewHolder extends DirectItemViewHolder {
public DirectItemLikeViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmLikeBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {}
@Override
protected boolean canForward() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,83 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLink;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLinkContext;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemLinkViewHolder extends DirectItemViewHolder {
private final LayoutDmLinkBinding binding;
public DirectItemLinkViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmLinkBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
final int width = windowWidth - margin - dmRadiusSmall;
final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();
layoutParams.width = width;
binding.preview.requestLayout();
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemLink link = item.getLink();
final DirectItemLinkContext linkContext = link.getLinkContext();
final String linkImageUrl = linkContext.getLinkImageUrl();
if (TextUtils.isEmpty(linkImageUrl)) {
binding.preview.setVisibility(View.GONE);
} else {
binding.preview.setVisibility(View.VISIBLE);
binding.preview.setImageURI(linkImageUrl);
}
if (TextUtils.isEmpty(linkContext.getLinkTitle())) {
binding.title.setVisibility(View.GONE);
} else {
binding.title.setVisibility(View.VISIBLE);
binding.title.setText(linkContext.getLinkTitle());
}
if (TextUtils.isEmpty(linkContext.getLinkSummary())) {
binding.summary.setVisibility(View.GONE);
} else {
binding.summary.setVisibility(View.VISIBLE);
binding.summary.setText(linkContext.getLinkSummary());
}
if (TextUtils.isEmpty(linkContext.getLinkUrl())) {
binding.url.setVisibility(View.GONE);
} else {
binding.url.setVisibility(View.VISIBLE);
binding.url.setText(linkContext.getLinkUrl());
}
binding.text.setText(link.getText());
setupListeners(linkContext);
}
private void setupListeners(final DirectItemLinkContext linkContext) {
setupRamboTextListeners(binding.text);
final View.OnClickListener onClickListener = v -> openURL(linkContext.getLinkUrl());
binding.preview.setOnClickListener(onClickListener);
// binding.preview.setOnLongClickListener(v -> itemView.performLongClick());
binding.title.setOnClickListener(onClickListener);
binding.summary.setOnClickListener(onClickListener);
binding.url.setOnClickListener(onClickListener);
}
@Override
protected boolean showBackground() {
return true;
}
}

View File

@ -0,0 +1,180 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import java.util.Objects;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemClip;
import awais.instagrabber.repositories.responses.directmessages.DirectItemFelixShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
private static final String TAG = DirectItemMediaShareViewHolder.class.getSimpleName();
private final LayoutDmMediaShareBinding binding;
private final RoundingParams incomingRoundingParams;
private final RoundingParams outgoingRoundingParams;
private DirectItemType itemType;
public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);
outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
binding.topBg.setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_media_share_top_incoming
: R.drawable.bg_media_share_top_outgoing);
Media media = getMedia(item);
if (media == null) return;
itemView.post(() -> {
setupUser(media);
setupTitle(media);
setupCaption(media);
});
itemView.post(() -> {
final MediaItemType mediaType = media.getMediaType();
setupTypeIndicator(mediaType);
if (mediaType == MediaItemType.MEDIA_TYPE_SLIDER) {
setupPreview(media.getCarouselMedia().get(0), messageDirection);
return;
}
setupPreview(media, messageDirection);
});
itemView.setOnClickListener(v -> openMedia(media));
}
private void setupTypeIndicator(final MediaItemType mediaType) {
final boolean showTypeIcon = mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER;
if (!showTypeIcon) {
binding.typeIcon.setVisibility(View.GONE);
} else {
binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_VIDEO
? R.drawable.ic_video_24
: R.drawable.ic_checkbox_multiple_blank_stroke);
}
}
private void setupPreview(@NonNull final Media media,
final MessageDirection messageDirection) {
final String url = ResponseBodyUtils.getThumbUrl(media);
if (Objects.equals(url, binding.mediaPreview.getTag())) {
return;
}
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;
binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.setRoundingParams(roundingParams)
.build());
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.mediaPreview.requestLayout();
binding.mediaPreview.setTag(url);
binding.mediaPreview.setImageURI(url);
}
private void setupCaption(@NonNull final Media media) {
final Caption caption = media.getCaption();
if (caption != null) {
binding.caption.setVisibility(View.VISIBLE);
binding.caption.setText(caption.getText());
binding.caption.setEllipsize(TextUtils.TruncateAt.END);
binding.caption.setMaxLines(2);
} else {
binding.caption.setVisibility(View.GONE);
}
}
private void setupTitle(@NonNull final Media media) {
final String title = media.getTitle();
if (!TextUtils.isEmpty(title)) {
binding.title.setVisibility(View.VISIBLE);
binding.title.setText(title);
} else {
binding.title.setVisibility(View.GONE);
}
}
private void setupUser(@NonNull final Media media) {
final User user = media.getUser();
if (user != null) {
binding.username.setVisibility(View.VISIBLE);
binding.profilePic.setVisibility(View.VISIBLE);
binding.username.setText(user.getUsername());
binding.profilePic.setImageURI(user.getProfilePicUrl());
} else {
binding.username.setVisibility(View.GONE);
binding.profilePic.setVisibility(View.GONE);
}
}
@Nullable
private Media getMedia(@NonNull final DirectItem item) {
Media media = null;
itemType = item.getItemType();
if (itemType == DirectItemType.MEDIA_SHARE) {
media = item.getMediaShare();
} else if (itemType == DirectItemType.CLIP) {
final DirectItemClip clip = item.getClip();
if (clip == null) return null;
media = clip.getClip();
} else if (itemType == DirectItemType.FELIX_SHARE) {
final DirectItemFelixShare felixShare = item.getFelixShare();
if (felixShare == null) return null;
media = felixShare.getVideo();
}
return media;
}
@Override
protected int getReactionsTranslationY() {
return reactionTranslationYType2;
}
@Override
public int getSwipeDirection() {
if (itemType != null && (itemType == DirectItemType.CLIP || itemType == DirectItemType.FELIX_SHARE)) {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
return super.getSwipeDirection();
}
}

View File

@ -0,0 +1,73 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmMediaBinding binding;
private final RoundingParams incomingRoundingParams;
private final RoundingParams outgoingRoundingParams;
public DirectItemMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);
outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;
binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final Media media = directItemModel.getMedia();
itemView.setOnClickListener(v -> openMedia(media));
final MediaItemType modelMediaType = media.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();
final int width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.width = width;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.mediaPreview.requestLayout();
binding.bgTime.getLayoutParams().width = width;
binding.bgTime.requestLayout();
final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);
binding.mediaPreview.setImageURI(thumbUrl);
}
}

View File

@ -0,0 +1,47 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemPlaceholderViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemPlaceholderViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final String text = String.format("%s: %s", directItemModel.getPlaceholder().getTitle(), directItemModel.getPlaceholder().getMessage());
binding.tvMessage.setText(text);
}
@Override
protected boolean showBackground() {
return true;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,125 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.drawee.view.SimpleDraweeView;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemProfileViewHolder extends DirectItemViewHolder {
private final LayoutDmProfileBinding binding;
private final ImmutableList<SimpleDraweeView> previewViews;
public DirectItemProfileViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmProfileBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
previewViews = ImmutableList.of(
binding.preview1,
binding.preview2,
binding.preview3,
binding.preview4,
binding.preview5,
binding.preview6
);
final Resources resources = itemView.getResources();
binding.preview4.setHierarchy(new GenericDraweeHierarchyBuilder(resources)
.setRoundingParams(RoundingParams.fromCornersRadii(0, 0, 0, dmRadius))
.build());
binding.preview6.setHierarchy(new GenericDraweeHierarchyBuilder(resources)
.setRoundingParams(RoundingParams.fromCornersRadii(0, 0, dmRadius, 0))
.build());
}
@Override
public void bindItem(@NonNull final DirectItem item,
final MessageDirection messageDirection) {
binding.getRoot().setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
if (item.getItemType() == DirectItemType.PROFILE) {
setProfile(item);
} else if (item.getItemType() == DirectItemType.LOCATION) {
setLocation(item);
} else {
return;
}
for (final SimpleDraweeView previewView : previewViews) {
previewView.setImageURI((String) null);
}
final List<Media> previewMedias = item.getPreviewMedias();
if (previewMedias.size() <= 0) {
binding.firstRow.setVisibility(View.GONE);
binding.secondRow.setVisibility(View.GONE);
return;
}
if (previewMedias.size() <= 3) {
binding.firstRow.setVisibility(View.VISIBLE);
binding.secondRow.setVisibility(View.GONE);
}
for (int i = 0; i < previewMedias.size(); i++) {
final Media previewMedia = previewMedias.get(i);
if (previewMedia == null) continue;
final String url = ResponseBodyUtils.getThumbUrl(previewMedia);
if (url == null) continue;
previewViews.get(i).setImageURI(url);
}
}
private void setProfile(@NonNull final DirectItem item) {
final User profile = item.getProfile();
if (profile == null) return;
binding.profilePic.setImageURI(profile.getProfilePicUrl());
binding.username.setText(profile.getUsername());
binding.fullName.setText(profile.getFullName());
binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE);
itemView.setOnClickListener(v -> openProfile(profile.getUsername()));
}
private void setLocation(@NonNull final DirectItem item) {
final Location location = item.getLocation();
if (location == null) return;
binding.profilePic.setVisibility(View.GONE);
binding.username.setText(location.getName());
final String address = location.getAddress();
if (!TextUtils.isEmpty(address)) {
binding.fullName.setText(address);
binding.fullName.setVisibility(View.VISIBLE);
} else {
binding.fullName.setVisibility(View.GONE);
}
binding.isVerified.setVisibility(View.GONE);
itemView.setOnClickListener(v -> openLocation(location.getPk()));
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,191 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenMediaViewMode;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxWidth;
public DirectItemRavenMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
maxWidth = windowWidth - margin - dmRadiusSmall;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVisualMedia visualMedia = directItemModel.getVisualMedia();
final Media media = visualMedia.getMedia();
if (media == null) return;
setExpiryInfo(visualMedia);
setPreview(visualMedia, messageDirection);
final boolean expired = media.getPk() == null;
if (expired) return;
itemView.setOnClickListener(v -> openMedia(media));
/*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||
TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
RavenExpiringMediaActionSummary mediaActionSummary = null;
if (visualMedia != null) {
mediaActionSummary = visualMedia.getExpiringMediaActionSummary();
}
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE);
int textRes = R.string.dms_inbox_raven_media_unknown;
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired;
if (!isExpired) {
if (mediaActionSummary != null) {
final ActionType expiringMediaType = mediaActionSummary.getType();
if (expiringMediaType == ActionType.DELIVERED)
textRes = R.string.dms_inbox_raven_media_delivered;
else if (expiringMediaType == ActionType.SENT)
textRes = R.string.dms_inbox_raven_media_sent;
else if (expiringMediaType == ActionType.OPENED)
textRes = R.string.dms_inbox_raven_media_opened;
else if (expiringMediaType == ActionType.REPLAYED)
textRes = R.string.dms_inbox_raven_media_replayed;
else if (expiringMediaType == ActionType.SENDING)
textRes = R.string.dms_inbox_raven_media_sending;
else if (expiringMediaType == ActionType.BLOCKED)
textRes = R.string.dms_inbox_raven_media_blocked;
else if (expiringMediaType == ActionType.SUGGESTED)
textRes = R.string.dms_inbox_raven_media_suggested;
else if (expiringMediaType == ActionType.SCREENSHOT)
textRes = R.string.dms_inbox_raven_media_screenshot;
else if (expiringMediaType == ActionType.CANNOT_DELIVER)
textRes = R.string.dms_inbox_raven_media_cant_deliver;
}
final RavenMediaViewMode ravenMediaViewMode = visualMedia.getViewType();
if (ravenMediaViewMode == RavenMediaViewMode.PERMANENT || ravenMediaViewMode == RavenMediaViewMode.REPLAYABLE) {
final MediaItemType mediaType = mediaModel.getMediaType();
textRes = -1;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
}
}
if (textRes != -1) {
binding.tvMessage.setText(context.getText(textRes));
binding.tvMessage.setVisibility(View.VISIBLE);
}*/
}
private void setExpiryInfo(final DirectItemVisualMedia visualMedia) {
final Media media = visualMedia.getMedia();
final RavenMediaViewMode viewMode = visualMedia.getViewMode();
if (viewMode != RavenMediaViewMode.PERMANENT) {
final MediaItemType mediaType = media.getMediaType();
final boolean expired = media.getPk() == null;
final int info;
switch (mediaType) {
case MEDIA_TYPE_IMAGE:
if (expired) {
info = R.string.raven_image_expired;
break;
}
info = R.string.raven_image_info;
break;
case MEDIA_TYPE_VIDEO:
if (expired) {
info = R.string.raven_video_expired;
break;
}
info = R.string.raven_video_info;
break;
default:
if (expired) {
info = R.string.raven_msg_expired;
break;
}
info = R.string.raven_msg_info;
break;
}
binding.expiryInfo.setVisibility(View.VISIBLE);
binding.expiryInfo.setText(info);
return;
}
binding.expiryInfo.setVisibility(View.GONE);
}
private void setPreview(final DirectItemVisualMedia visualMedia,
final MessageDirection messageDirection) {
final Media media = visualMedia.getMedia();
final boolean expired = media.getPk() == null;
if (expired) {
binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
return;
}
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING
? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)
: RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final MediaItemType modelMediaType = media.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.preview.requestLayout();
final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);
binding.preview.setImageURI(thumbUrl);
}
@Override
protected boolean allowLongClick() {
return false; // disabling until confirmed
}
}

View File

@ -0,0 +1,173 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmReelShareBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemReelShareViewHolder extends DirectItemViewHolder {
private final LayoutDmReelShareBinding binding;
public DirectItemReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmReelShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemReelShare reelShare = item.getReelShare();
final String type = reelShare.getType();
if (type == null) return;
final boolean isSelf = isSelf(item);
final Media media = reelShare.getMedia();
if (media == null) return;
final User user = media.getUser();
if (user == null) return;
final boolean expired = media.getMediaType() == null;
if (expired) {
binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
binding.quoteLine.setVisibility(View.GONE);
binding.reaction.setVisibility(View.GONE);
} else {
binding.preview.setVisibility(View.VISIBLE);
binding.typeIcon.setVisibility(View.VISIBLE);
binding.quoteLine.setVisibility(View.VISIBLE);
binding.reaction.setVisibility(View.VISIBLE);
}
setGravity(messageDirection, expired);
if (type.equals("reply")) {
setReply(messageDirection, reelShare, isSelf);
}
if (type.equals("reaction")) {
setReaction(messageDirection, reelShare, isSelf, expired);
}
if (type.equals("mention")) {
setMention(isSelf);
}
if (!expired) {
setPreview(media);
itemView.setOnClickListener(v -> openMedia(media));
}
}
private void setGravity(final MessageDirection messageDirection, final boolean expired) {
final boolean isIncoming = messageDirection == MessageDirection.INCOMING;
binding.shareInfo.setGravity(isIncoming ? Gravity.START : Gravity.END);
if (!expired) {
binding.quoteLine.setVisibility(isIncoming ? View.VISIBLE : View.GONE);
binding.quoteLineEnd.setVisibility(isIncoming ? View.GONE : View.VISIBLE);
}
final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();
layoutParams.horizontalBias = isIncoming ? 0 : 1;
final ConstraintLayout.LayoutParams messageLayoutParams = (ConstraintLayout.LayoutParams) binding.message.getLayoutParams();
messageLayoutParams.startToStart = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;
messageLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;
messageLayoutParams.setMarginStart(isIncoming ? messageInfoPaddingSmall : 0);
messageLayoutParams.setMarginEnd(isIncoming ? 0 : messageInfoPaddingSmall);
final ConstraintLayout.LayoutParams reactionLayoutParams = (ConstraintLayout.LayoutParams) binding.reaction.getLayoutParams();
final int previewId = binding.preview.getId();
if (isIncoming) {
reactionLayoutParams.startToEnd = previewId;
reactionLayoutParams.endToEnd = previewId;
reactionLayoutParams.startToStart = ConstraintLayout.LayoutParams.UNSET;
reactionLayoutParams.endToStart = ConstraintLayout.LayoutParams.UNSET;
} else {
reactionLayoutParams.startToStart = previewId;
reactionLayoutParams.endToStart = previewId;
reactionLayoutParams.startToEnd = ConstraintLayout.LayoutParams.UNSET;
reactionLayoutParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;
}
}
private void setReply(final MessageDirection messageDirection,
final DirectItemReelShare reelShare,
final boolean isSelf) {
final int info = isSelf ? R.string.replied_story_outgoing : R.string.replied_story_incoming;
binding.shareInfo.setText(info);
binding.reaction.setVisibility(View.GONE);
final String text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.message.setVisibility(View.GONE);
return;
}
setMessage(messageDirection, text);
}
private void setReaction(final MessageDirection messageDirection,
final DirectItemReelShare reelShare,
final boolean isSelf,
final boolean expired) {
final int info = isSelf ? R.string.reacted_story_outgoing : R.string.reacted_story_incoming;
binding.shareInfo.setText(info);
binding.message.setVisibility(View.GONE);
final String text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.reaction.setVisibility(View.GONE);
return;
}
if (expired) {
setMessage(messageDirection, text);
return;
}
binding.reaction.setVisibility(View.VISIBLE);
binding.reaction.setText(text);
}
private void setMention(final boolean isSelf) {
final int info = isSelf ? R.string.mentioned_story_outgoing : R.string.mentioned_story_incoming;
binding.shareInfo.setText(info);
binding.message.setVisibility(View.GONE);
binding.reaction.setVisibility(View.GONE);
}
private void setMessage(final MessageDirection messageDirection, final String text) {
binding.message.setVisibility(View.VISIBLE);
binding.message.setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
binding.message.setText(text);
}
private void setPreview(final Media media) {
final MediaItemType mediaType = media.getMediaType();
if (mediaType == null) return;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE : View.GONE);
final RoundingParams roundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadiusSmall, dmRadiusSmall, dmRadiusSmall);
binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.build());
final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);
binding.preview.setImageURI(thumbUrl);
}
@Override
protected boolean canForward() {
return false;
}
}

View File

@ -0,0 +1,120 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemStoryShareViewHolder extends DirectItemViewHolder {
private final LayoutDmStoryShareBinding binding;
public DirectItemStoryShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmStoryShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final Resources resources = itemView.getResources();
int format = R.string.story_share;
final String reelType = item.getStoryShare().getReelType();
if (reelType == null || item.getStoryShare().getMedia() == null) {
setExpiredStoryInfo(item);
return;
}
if (reelType.equals("highlight_reel")) {
format = R.string.story_share_highlight;
}
final User user = item.getStoryShare().getMedia().getUser();
final String info = resources.getString(format, user != null ? user.getUsername() : "");
binding.shareInfo.setText(info);
binding.text.setVisibility(View.GONE);
binding.ivMediaPreview.setController(null);
final DirectItemStoryShare storyShare = item.getStoryShare();
if (storyShare == null) return;
setText(storyShare);
final Media media = storyShare.getMedia();
setupPreview(messageDirection, media);
itemView.setOnClickListener(v -> openStory(storyShare));
}
private void setupPreview(final MessageDirection messageDirection, final Media storyShareMedia) {
final MediaItemType mediaType = storyShareMedia.getMediaType();
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING
? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)
: RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
binding.ivMediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
storyShareMedia.getOriginalHeight(),
storyShareMedia.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
final String thumbUrl = ResponseBodyUtils.getThumbUrl(storyShareMedia);
binding.ivMediaPreview.setImageURI(thumbUrl);
}
private void setText(final DirectItemStoryShare storyShare) {
final String text = storyShare.getText();
if (!TextUtils.isEmpty(text)) {
binding.text.setText(text);
binding.text.setVisibility(View.VISIBLE);
return;
}
binding.text.setVisibility(View.GONE);
}
private void setExpiredStoryInfo(final DirectItem item) {
binding.shareInfo.setText(item.getStoryShare().getTitle());
binding.text.setVisibility(View.VISIBLE);
binding.text.setText(item.getStoryShare().getMessage());
binding.ivMediaPreview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
}
@Override
protected boolean canForward() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,38 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemTextViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemTextViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
@NonNull final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final String text = directItemModel.getText();
if (text == null) return;
binding.tvMessage.setText(text);
setupRamboTextListeners(binding.tvMessage);
}
@Override
protected boolean showBackground() {
return true;
}
}

View File

@ -0,0 +1,90 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemActionLog;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVideoCallEvent;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemVideoCallEventViewHolder extends DirectItemViewHolder {
private final LayoutDmActionLogBinding binding;
public DirectItemVideoCallEventViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmActionLogBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVideoCallEvent videoCallEvent = directItemModel.getVideoCallEvent();
final String text = videoCallEvent.getDescription();
final SpannableStringBuilder sb = new SpannableStringBuilder(text);
final List<DirectItemActionLog.TextRange> textAttributes = videoCallEvent.getTextAttributes();
if (textAttributes != null && !textAttributes.isEmpty()) {
for (final DirectItemActionLog.TextRange textAttribute : textAttributes) {
if (!TextUtils.isEmpty(textAttribute.getColor())) {
final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));
sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
if (!TextUtils.isEmpty(textAttribute.getIntent())) {
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull final View widget) {
}
};
sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
binding.tvMessage.setMaxLines(1);
binding.tvMessage.setText(sb);
}
@Override
protected boolean allowMessageDirectionGravity() {
return false;
}
@Override
protected boolean showUserDetailsInGroup() {
return false;
}
@Override
protected boolean showMessageInfo() {
return false;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,608 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.text.format.DateFormat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.widget.ImageViewCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;
import com.google.android.material.transition.MaterialFade;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemInternalLongClickListener;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.customviews.DirectItemFrameLayout;
import awais.instagrabber.customviews.RamboTextViewV2;
import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback.SwipeableViewHolder;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.DMUtils;
import awais.instagrabber.utils.DeepLinkParser;
import awais.instagrabber.utils.ResponseBodyUtils;
public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
private static final String TAG = DirectItemViewHolder.class.getSimpleName();
// private static final List<Integer> THREAD_CHANGING_OPTIONS = ImmutableList.of(R.id.unsend);
private final LayoutDmBaseBinding binding;
private final User currentUser;
private final DirectThread thread;
private final int groupMessageWidth;
private final List<Long> userIds;
private final DirectItemCallback callback;
private final int reactionAdjustMargin;
private final AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
protected final int margin;
protected final int dmRadius;
protected final int dmRadiusSmall;
protected final int messageInfoPaddingSmall;
protected final int mediaImageMaxHeight;
protected final int windowWidth;
protected final int mediaImageMaxWidth;
protected final int reactionTranslationYType1;
protected final int reactionTranslationYType2;
private boolean selected = false;
private DirectItemInternalLongClickListener longClickListener;
private DirectItem item;
private ViewPropertyAnimator shrinkGrowAnimator;
private MessageDirection messageDirection;
// private View.OnLayoutChangeListener layoutChangeListener;
public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
@NonNull final User currentUser,
@NonNull final DirectThread thread,
@NonNull final DirectItemCallback callback) {
super(binding.getRoot());
this.binding = binding;
this.currentUser = currentUser;
this.thread = thread;
this.callback = callback;
userIds = thread.getUsers()
.stream()
.map(User::getPk)
.collect(Collectors.toList());
binding.ivProfilePic.setVisibility(thread.isGroup() ? View.VISIBLE : View.GONE);
binding.ivProfilePic.setOnClickListener(null);
final Resources resources = itemView.getResources();
margin = resources.getDimensionPixelSize(R.dimen.dm_message_item_margin);
final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_message_item_avatar_size);
dmRadius = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius);
dmRadiusSmall = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius_small);
messageInfoPaddingSmall = resources.getDimensionPixelSize(R.dimen.dm_message_info_padding_small);
windowWidth = resources.getDisplayMetrics().widthPixels;
mediaImageMaxHeight = resources.getDimensionPixelSize(R.dimen.dm_media_img_max_height);
reactionAdjustMargin = resources.getDimensionPixelSize(R.dimen.dm_reaction_adjust_margin);
final int groupWidthCorrection = avatarSize + messageInfoPaddingSmall * 3;
mediaImageMaxWidth = windowWidth - margin - (thread.isGroup() ? groupWidthCorrection : messageInfoPaddingSmall * 2);
// messageInfoPaddingSmall is used cuz it's also 4dp, 1 avatar margin + 2 paddings = 3
groupMessageWidth = windowWidth - margin - groupWidthCorrection;
reactionTranslationYType1 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_1);
reactionTranslationYType2 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_2);
}
public void bind(final int position, final DirectItem item) {
this.item = item;
messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
// Asynchronous binding causes some weird behaviour
// itemView.post(() -> bindBase(item, messageDirection, position));
// itemView.post(() -> bindItem(item, messageDirection));
// itemView.post(() -> setupLongClickListener(position, messageDirection));
bindBase(item, messageDirection, position);
bindItem(item, messageDirection);
setupLongClickListener(position, messageDirection);
}
private void bindBase(final DirectItem item, final MessageDirection messageDirection, final int position) {
final FrameLayout.LayoutParams containerLayoutParams = (FrameLayout.LayoutParams) binding.container.getLayoutParams();
final DirectItemType itemType = item.getItemType();
setMessageDirectionGravity(messageDirection, containerLayoutParams);
setGroupUserDetails(item, messageDirection);
setBackground(messageDirection);
setMessageInfo(item, messageDirection);
if (itemType == DirectItemType.REEL_SHARE) {
containerLayoutParams.setMarginStart(0);
containerLayoutParams.setMarginEnd(0);
}
if (itemType == DirectItemType.TEXT || itemType == DirectItemType.LINK) {
binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall);
} else {
if (showMessageInfo()) {
binding.messageInfo.setPadding(0, 0, messageInfoPaddingSmall, dmRadiusSmall);
}
}
setupReply(item, messageDirection);
setReactions(item, position);
if (item.getRepliedToMessage() == null && item.showForwardAttribution()) {
setForwardInfo(messageDirection);
}
}
private void setBackground(final MessageDirection messageDirection) {
if (showBackground()) {
binding.background.setBackgroundResource(messageDirection == MessageDirection.INCOMING ? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
return;
}
binding.background.setBackgroundResource(0);
}
private void setGroupUserDetails(final DirectItem item, final MessageDirection messageDirection) {
if (showUserDetailsInGroup()) {
binding.ivProfilePic.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
binding.tvUsername.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
if (messageDirection == MessageDirection.INCOMING && thread.isGroup()) {
final User user = getUser(item.getUserId(), thread.getUsers());
if (user != null) {
binding.tvUsername.setText(user.getUsername());
binding.ivProfilePic.setImageURI(user.getProfilePicUrl());
}
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.chatMessageLayout.getLayoutParams();
layoutParams.matchConstraintMaxWidth = groupMessageWidth;
binding.chatMessageLayout.setLayoutParams(layoutParams);
}
return;
}
binding.ivProfilePic.setVisibility(View.GONE);
binding.tvUsername.setVisibility(View.GONE);
}
private void setMessageDirectionGravity(final MessageDirection messageDirection,
final FrameLayout.LayoutParams containerLayoutParams) {
if (allowMessageDirectionGravity()) {
containerLayoutParams.setMarginStart(messageDirection == MessageDirection.OUTGOING ? margin : 0);
containerLayoutParams.setMarginEnd(messageDirection == MessageDirection.INCOMING ? margin : 0);
containerLayoutParams.gravity = messageDirection == MessageDirection.INCOMING ? Gravity.START : Gravity.END;
return;
}
containerLayoutParams.gravity = Gravity.CENTER;
}
private void setMessageInfo(final DirectItem item, final MessageDirection messageDirection) {
if (showMessageInfo()) {
binding.messageInfo.setVisibility(View.VISIBLE);
binding.deliveryStatus.setVisibility(messageDirection == MessageDirection.OUTGOING ? View.VISIBLE : View.GONE);
binding.messageTime.setText(DateFormat.getTimeFormat(itemView.getContext()).format(item.getDate()));
if (messageDirection == MessageDirection.OUTGOING) {
if (item.isPending()) {
binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
} else {
final boolean read = DMUtils.isRead(item,
thread.getLastSeenAt(),
userIds
);
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
ImageViewCompat.setImageTintList(
binding.deliveryStatus,
ColorStateList.valueOf(itemView.getResources().getColor(read ? R.color.blue_500 : R.color.grey_500))
);
}
}
return;
}
binding.messageInfo.setVisibility(View.GONE);
}
private void setupReply(final DirectItem item, final MessageDirection messageDirection) {
if (item.getRepliedToMessage() != null) {
setReply(item, messageDirection, thread.getUsers());
} else {
binding.quoteLine.setVisibility(View.GONE);
binding.replyContainer.setVisibility(View.GONE);
binding.replyInfo.setVisibility(View.GONE);
}
}
private void setReply(final DirectItem item,
final MessageDirection messageDirection,
final List<User> users) {
final DirectItem replied = item.getRepliedToMessage();
final DirectItemType itemType = replied.getItemType();
final Resources resources = itemView.getResources();
String text = null;
String url = null;
switch (itemType) {
case TEXT:
text = replied.getText();
break;
case LINK:
text = replied.getLink().getText();
break;
case PLACEHOLDER:
text = replied.getPlaceholder().getMessage();
break;
case MEDIA:
url = ResponseBodyUtils.getThumbUrl(replied.getMedia());
break;
case RAVEN_MEDIA:
url = ResponseBodyUtils.getThumbUrl(replied.getVisualMedia().getMedia());
break;
case VOICE_MEDIA:
text = resources.getString(R.string.voice_message);
break;
case MEDIA_SHARE:
Media mediaShare = replied.getMediaShare();
if (mediaShare.getMediaType() == MediaItemType.MEDIA_TYPE_SLIDER) {
mediaShare = mediaShare.getCarouselMedia().get(0);
}
url = ResponseBodyUtils.getThumbUrl(mediaShare);
break;
case REEL_SHARE:
text = replied.getReelShare().getText();
break;
// Below types cannot be replied to
// case LIKE:
// text = "❤️";
// break;
// case PROFILE:
// text = "@" + replied.getProfile().getUsername();
// break;
// case CLIP:
// url = ResponseBodyUtils.getThumbUrl(replied.getClip().getClip().getImageVersions2());
// break;
// case FELIX_SHARE:
// url = ResponseBodyUtils.getThumbUrl(replied.getFelixShare().getVideo().getImageVersions2());
// break;
// case STORY_SHARE:
// final DirectItemMedia media = replied.getStoryShare().getMedia();
// if (media == null) break;
// url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
// break;
// case LOCATION
}
if (text == null && url == null) {
binding.quoteLine.setVisibility(View.GONE);
binding.replyContainer.setVisibility(View.GONE);
binding.replyInfo.setVisibility(View.GONE);
return;
}
setReplyGravity(messageDirection);
final String info = setReplyInfo(item, replied, users, resources);
binding.replyInfo.setVisibility(View.VISIBLE);
binding.replyInfo.setText(info);
binding.quoteLine.setVisibility(View.VISIBLE);
binding.replyContainer.setVisibility(View.VISIBLE);
if (url != null) {
binding.replyText.setVisibility(View.GONE);
binding.replyImage.setVisibility(View.VISIBLE);
binding.replyImage.setImageURI(url);
return;
}
binding.replyImage.setVisibility(View.GONE);
final Drawable background = binding.replyText.getBackground().mutate();
background.setTint(replied.getUserId() != currentUser.getPk()
? resources.getColor(R.color.grey_600)
: resources.getColor(R.color.deep_purple_400));
binding.replyText.setBackgroundDrawable(background);
binding.replyText.setVisibility(View.VISIBLE);
binding.replyText.setText(text);
}
private String setReplyInfo(final DirectItem item,
final DirectItem replied,
final List<User> users,
final Resources resources) {
final long repliedToUserId = replied.getUserId();
if (repliedToUserId == item.getUserId() && item.getUserId() == currentUser.getPk()) {
// User replied to own message
return resources.getString(R.string.replied_to_yourself);
}
if (repliedToUserId == item.getUserId()) {
// opposite user replied to their own message
return resources.getString(R.string.replied_to_themself);
}
final User user = getUser(repliedToUserId, users);
final String repliedToUsername = user != null ? user.getUsername() : "";
if (item.getUserId() == currentUser.getPk()) {
return thread.isGroup()
? resources.getString(R.string.replied_you_group, repliedToUsername)
: resources.getString(R.string.replied_you);
}
if (repliedToUserId == currentUser.getPk()) {
return resources.getString(R.string.replied_to_you);
}
return resources.getString(R.string.replied_group, repliedToUsername);
}
private void setForwardInfo(final MessageDirection direction) {
binding.replyInfo.setVisibility(View.VISIBLE);
binding.replyInfo.setText(direction == MessageDirection.OUTGOING ? R.string.forward_outgoing : R.string.forward_incoming);
}
private void setReplyGravity(final MessageDirection messageDirection) {
final boolean isIncoming = messageDirection == MessageDirection.INCOMING;
final ConstraintLayout.LayoutParams quoteLineLayoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();
final ConstraintLayout.LayoutParams replyContainerLayoutParams = (ConstraintLayout.LayoutParams) binding.replyContainer.getLayoutParams();
final ConstraintLayout.LayoutParams replyInfoLayoutParams = (ConstraintLayout.LayoutParams) binding.replyInfo.getLayoutParams();
final int profilePicId = binding.ivProfilePic.getId();
final int replyContainerId = binding.replyContainer.getId();
final int quoteLineId = binding.quoteLine.getId();
quoteLineLayoutParams.startToEnd = isIncoming ? profilePicId : replyContainerId;
quoteLineLayoutParams.endToStart = isIncoming ? replyContainerId : ConstraintLayout.LayoutParams.UNSET;
quoteLineLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;
replyContainerLayoutParams.startToEnd = isIncoming ? quoteLineId : profilePicId;
replyContainerLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;
replyContainerLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
replyInfoLayoutParams.startToEnd = isIncoming ? quoteLineId : ConstraintLayout.LayoutParams.UNSET;
replyInfoLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
}
private void setReactions(final DirectItem item, final int position) {
binding.getRoot().post(() -> {
MaterialFade materialFade = new MaterialFade();
materialFade.addTarget(binding.emojis);
TransitionManager.beginDelayedTransition(binding.getRoot(), materialFade);
final DirectItemReactions reactions = item.getReactions();
final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;
if (emojis == null || emojis.isEmpty()) {
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, 0);
binding.reactionsWrapper.setVisibility(View.GONE);
return;
}
binding.reactionsWrapper.setVisibility(View.VISIBLE);
binding.reactionsWrapper.setTranslationY(getReactionsTranslationY());
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, reactionAdjustMargin);
binding.emojis.setEmojis(emojis.stream()
.map(DirectItemEmojiReaction::getEmoji)
.collect(Collectors.toList()));
// binding.emojis.setEmojis(ImmutableList.of("😣",
// "😖",
// "😫",
// "😩",
// "🥺",
// "😢",
// "😭",
// "😤",
// "😠",
// "😡",
// "🤬"));
binding.emojis.setOnClickListener(v -> callback.onReactionClick(item, position));
// final List<DirectUser> reactedUsers = emojis.stream()
// .map(DirectItemEmojiReaction::getSenderId)
// .distinct()
// .map(userId -> getUser(userId, users))
// .collect(Collectors.toList());
// for (final DirectUser user : reactedUsers) {
// if (user == null) continue;
// final ProfilePicView profilePicView = new ProfilePicView(itemView.getContext());
// profilePicView.setSize(ProfilePicView.Size.TINY);
// profilePicView.setImageURI(user.getProfilePicUrl());
// binding.reactions.addView(profilePicView);
// }
});
}
protected boolean isSelf(final DirectItem directItem) {
return directItem.getUserId() == currentUser.getPk();
}
public void setItemView(final View view) {
this.binding.message.addView(view);
}
public abstract void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection);
@Nullable
protected User getUser(final long userId, final List<User> users) {
if (userId == currentUser.getPk()) {
return currentUser;
}
if (users == null) return null;
for (final User user : users) {
if (userId != user.getPk()) continue;
return user;
}
return null;
}
protected boolean allowMessageDirectionGravity() {
return true;
}
protected boolean showUserDetailsInGroup() {
return true;
}
protected boolean showBackground() {
return false;
}
protected boolean showMessageInfo() {
return true;
}
protected boolean allowLongClick() {
return true;
}
protected boolean allowReaction() {
return true;
}
protected boolean canForward() {
return true;
}
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return null;
}
protected int getReactionsTranslationY() {
return reactionTranslationYType1;
}
@CallSuper
public void cleanup() {
// if (layoutChangeListener != null) {
// binding.container.removeOnLayoutChangeListener(layoutChangeListener);
// }
}
protected void setupRamboTextListeners(@NonNull final RamboTextViewV2 textView) {
textView.addOnHashtagListener(autoLinkItem -> callback.onHashtagClick(autoLinkItem.getOriginalText().trim()));
textView.addOnMentionClickListener(autoLinkItem -> openProfile(autoLinkItem.getOriginalText().trim()));
textView.addOnEmailClickListener(autoLinkItem -> callback.onEmailClick(autoLinkItem.getOriginalText().trim()));
textView.addOnURLClickListener(autoLinkItem -> openURL(autoLinkItem.getOriginalText().trim()));
}
protected void openProfile(final String username) {
callback.onMentionClick(username);
}
protected void openLocation(final long locationId) {
callback.onLocationClick(locationId);
}
protected void openURL(final String url) {
callback.onURLClick(url);
}
protected void openMedia(final Media media) {
callback.onMediaClick(media);
}
protected void openStory(final DirectItemStoryShare storyShare) {
callback.onStoryClick(storyShare);
}
protected void handleDeepLink(final String deepLinkText) {
if (deepLinkText == null) return;
final DeepLinkParser.DeepLink deepLink = DeepLinkParser.parse(deepLinkText);
if (deepLink == null) return;
switch (deepLink.getType()) {
case USER:
callback.onMentionClick(deepLink.getValue());
break;
}
}
@SuppressLint("ClickableViewAccessibility")
private void setupLongClickListener(final int position, final MessageDirection messageDirection) {
if (!allowLongClick()) return;
binding.getRoot().setOnItemLongClickListener(new DirectItemFrameLayout.OnItemLongClickListener() {
@Override
public void onLongClickStart(final View view) {
itemView.post(() -> shrink());
}
@Override
public void onLongClickCancel(final View view) {
itemView.post(() -> grow());
}
@Override
public void onLongClick(final View view, final float x, final float y) {
// if (longClickListener == null) return false;
// longClickListener.onLongClick(position, this);
itemView.post(() -> grow());
setSelected(true);
showLongClickOptions(new Point((int) x, (int) y), messageDirection);
}
});
}
private void showLongClickOptions(final Point location, final MessageDirection messageDirection) {
final List<DirectItemContextMenu.MenuItem> longClickOptions = getLongClickOptions();
final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();
if (longClickOptions != null) {
builder.addAll(longClickOptions);
}
if (canForward()) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.forward, R.string.forward));
}
if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend));
}
final DirectItemType itemType = item.getItemType();
switch (itemType) {
case ANIMATED_MEDIA:
builder.add(new DirectItemContextMenu.MenuItem(R.id.detail, R.string.dms_inbox_giphy));
break;
case VOICE_MEDIA:
builder.add(new DirectItemContextMenu.MenuItem(R.id.detail, R.string.action_download));
break;
}
final boolean showReactions = thread.getInputMode() != 1 && allowReaction();
final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build();
if (!showReactions && menuItems.isEmpty()) return;
final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems);
menu.setOnDismissListener(() -> setSelected(false));
menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji));
menu.setOnOptionSelectListener(itemId -> callback.onOptionSelect(item, itemId));
menu.show(itemView, location);
}
public void setLongClickListener(final DirectItemInternalLongClickListener longClickListener) {
this.longClickListener = longClickListener;
}
public void setSelected(final boolean selected) {
this.selected = selected;
}
private void shrink() {
if (shrinkGrowAnimator != null) {
shrinkGrowAnimator.cancel();
}
shrinkGrowAnimator = itemView.animate()
.scaleX(0.8f)
.scaleY(0.8f)
.setInterpolator(accelerateDecelerateInterpolator)
.setDuration(ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout());
shrinkGrowAnimator.start();
}
private void grow() {
if (shrinkGrowAnimator != null) {
shrinkGrowAnimator.cancel();
}
shrinkGrowAnimator = itemView.animate()
.scaleX(1f)
.scaleY(1f)
.setInterpolator(accelerateDecelerateInterpolator)
.setDuration(200)
.withEndAction(() -> shrinkGrowAnimator = null);
shrinkGrowAnimator.start();
}
@Override
public int getSwipeDirection() {
if (item == null || messageDirection == null) return ItemTouchHelper.ACTION_STATE_IDLE;
return messageDirection == MessageDirection.OUTGOING ? ItemTouchHelper.START : ItemTouchHelper.END;
}
public enum MessageDirection {
INCOMING,
OUTGOING
}
}

View File

@ -0,0 +1,190 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.common.primitives.Floats;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.repositories.responses.Audio;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
import static com.google.android.exoplayer2.C.TIME_UNSET;
public class DirectItemVoiceMediaViewHolder extends DirectItemViewHolder {
private static final String TAG = "DirectItemVoiceMediaVH";
private final LayoutDmVoiceMediaBinding binding;
private final DefaultDataSourceFactory dataSourceFactory;
private SimpleExoPlayer player;
private Handler handler;
private Runnable positionChecker;
private Player.EventListener listener;
public DirectItemVoiceMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmVoiceMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), "instagram");
setItemView(binding.getRoot());
binding.voiceMedia.getLayoutParams().width = mediaImageMaxWidth;
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVoiceMedia voiceMedia = directItemModel.getVoiceMedia();
if (voiceMedia == null) return;
final Media media = voiceMedia.getMedia();
if (media == null) return;
final Audio audio = media.getAudio();
if (audio == null) return;
final List<Float> waveformData = audio.getWaveformData();
binding.waveformSeekBar.setSample(Floats.toArray(waveformData));
binding.waveformSeekBar.setEnabled(false);
final String text = String.format("%s/%s", TextUtils.millisToTimeString(0), TextUtils.millisToTimeString(audio.getDuration()));
binding.duration.setText(text);
final AudioItemState audioItemState = new AudioItemState();
player = new SimpleExoPlayer.Builder(itemView.getContext()).build();
player.setVolume(1);
player.setPlayWhenReady(true);
player.setRepeatMode(Player.REPEAT_MODE_OFF);
handler = new Handler();
final long initialDelay = 0;
final long recurringDelay = 60;
positionChecker = new Runnable() {
@Override
public void run() {
if (handler != null) {
handler.removeCallbacks(this);
}
if (player == null) return;
final long currentPosition = player.getCurrentPosition();
final long duration = player.getDuration();
// Log.d(TAG, "currentPosition: " + currentPosition + ", duration: " + duration);
if (duration == TIME_UNSET) return;
// final float progress = ((float) currentPosition / duration /* * 100 */);
final int progress = (int) ((float) currentPosition / duration * 100);
// Log.d(TAG, "progress: " + progress);
final String text = String.format("%s/%s", TextUtils.millisToTimeString(currentPosition), TextUtils.millisToTimeString(duration));
binding.duration.setText(text);
binding.waveformSeekBar.setProgress(progress);
if (handler != null) {
handler.postDelayed(this, recurringDelay);
}
}
};
player.addListener(listener = new Player.EventListener() {
@Override
public void onPlaybackStateChanged(final int state) {
if (!audioItemState.isPrepared() && state == Player.STATE_READY) {
binding.playPause.setIconResource(R.drawable.ic_round_pause_24);
audioItemState.setPrepared(true);
binding.playPause.setVisibility(View.VISIBLE);
binding.progressBar.setVisibility(View.GONE);
if (handler != null) {
handler.postDelayed(positionChecker, initialDelay);
}
return;
}
if (state == Player.STATE_ENDED) {
// binding.waveformSeekBar.setProgressInPercentage(0);
binding.waveformSeekBar.setProgress(0);
binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);
if (handler != null) {
handler.removeCallbacks(positionChecker);
}
}
}
@Override
public void onPlayerError(final ExoPlaybackException error) {
Log.e(TAG, "onPlayerError: ", error);
}
});
final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
final MediaItem mediaItem = MediaItem.fromUri(audio.getAudioSrc());
final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
player.setMediaSource(mediaSource);
binding.playPause.setOnClickListener(v -> {
if (player == null) return;
if (!audioItemState.isPrepared()) {
player.prepare();
binding.playPause.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
return;
}
if (player.isPlaying()) {
binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);
player.pause();
return;
}
binding.playPause.setIconResource(R.drawable.ic_round_pause_24);
if (player.getPlaybackState() == Player.STATE_ENDED) {
player.seekTo(0);
if (handler != null) {
handler.postDelayed(positionChecker, initialDelay);
}
}
binding.waveformSeekBar.setEnabled(true);
player.play();
});
}
@Override
public void cleanup() {
super.cleanup();
if (handler != null && positionChecker != null) {
handler.removeCallbacks(positionChecker);
handler = null;
positionChecker = null;
}
if (player != null) {
player.release();
if (listener != null) {
player.removeListener(listener);
}
player = null;
}
}
@Override
protected boolean canForward() {
return false;
}
private static class AudioItemState {
private boolean prepared;
private AudioItemState() {}
public boolean isPrepared() {
return prepared;
}
public void setPrepared(final boolean prepared) {
this.prepared = prepared;
}
}
}

View File

@ -1,32 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageActionLogViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageActionLogViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final String text = directItemModel.getActionLogModel().getDescription();
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + text + "</small>", FROM_HTML_MODE_COMPACT));
binding.tvMessage.setVisibility(View.VISIBLE);
}
}

View File

@ -1,57 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.facebook.drawee.backends.pipeline.Fresco;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageAnimatedMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmAnimatedMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmAnimatedMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin();
setItemView(binding.getRoot());
setupForAnimatedMedia();
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = directItemModel.getAnimatedMediaModel();
final String url = animatedMediaModel.getWebpUrl();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
animatedMediaModel.getHeight(),
animatedMediaModel.getWidth(),
maxHeight,
maxWidth
);
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();
final int width = widthHeight.first != null ? widthHeight.first : 0;
final int height = widthHeight.second != null ? widthHeight.second : 0;
layoutParams.width = width;
layoutParams.height = height;
binding.ivAnimatedMessage.requestLayout();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(url)
.setAutoPlayAnimations(true)
.build());
}
}

View File

@ -1,30 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageDefaultViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageDefaultViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
}
}

View File

@ -1,100 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolder {
private static final int MESSAGE_INCOMING = 69;
private static final int MESSAGE_OUTGOING = 420;
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(
CookieUtils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)));
private final LayoutDmBaseBinding binding;
private final int itemMargin;
public DirectMessageItemViewHolder(@NonNull final LayoutDmBaseBinding binding, @NonNull final View.OnClickListener onClickListener) {
super(binding.getRoot());
this.binding = binding;
binding.ivProfilePic.setOnClickListener(onClickListener);
binding.messageCard.setOnClickListener(onClickListener);
// final String strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you);
itemMargin = Utils.displayMetrics.widthPixels / 5;
}
public void bind(final DirectItemModel directItemModel, final List<ProfileModel> users, final List<ProfileModel> leftUsers) {
final ProfileModel user = getUser(directItemModel.getUserId(), users, leftUsers);
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING;
final RecyclerView.LayoutParams itemViewLayoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams();
itemViewLayoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0,
type == MESSAGE_INCOMING ? itemMargin : 0, 0);
final ViewGroup messageCardParent = (ViewGroup) binding.messageCard.getParent();
binding.contentContainer.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
CharSequence text = "?";
if (user != null && user != myProfileHolder) {
text = user.getUsername();
} else if (user == myProfileHolder) text = "";
text = (TextUtils.isEmpty(text) ? "" : text + " - ") + directItemModel.getDateTime();
binding.tvUsername.setText(text);
binding.tvUsername.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE);
binding.ivProfilePic.setTag(user);
binding.likedContainer.setVisibility(directItemModel.isLiked() ? View.VISIBLE : View.GONE);
messageCardParent.setTag(directItemModel);
binding.messageCard.setTag(directItemModel);
if (type == MESSAGE_INCOMING && user != null) {
binding.ivProfilePic.setImageURI(user.getSdProfilePic());
}
bindItem(directItemModel);
}
public void setItemView(final View view) {
this.binding.messageCard.addView(view);
}
public int getItemMargin() {
return itemMargin;
}
public abstract void bindItem(final DirectItemModel directItemModel);
@Nullable
private ProfileModel getUser(final long userId, final List<ProfileModel> users, final List<ProfileModel> leftUsers) {
if (users != null) {
ProfileModel result = myProfileHolder;
for (final ProfileModel user : users) {
if (Long.toString(userId).equals(user.getId())) result = user;
}
if (leftUsers != null)
for (final ProfileModel leftUser : leftUsers) {
if (Long.toString(userId).equals(leftUser.getId())) result = leftUser;
}
return result;
}
return null;
}
protected void setupForAnimatedMedia() {
binding.messageCard.setCardElevation(0);
binding.messageCard.setCardBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
}
}

View File

@ -1,46 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageLinkViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmLinkBinding binding;
public DirectMessageLinkViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmLinkBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemLinkModel link = directItemModel.getLinkModel();
final DirectItemModel.DirectItemLinkContext linkContext = link.getLinkContext();
final String linkImageUrl = linkContext.getLinkImageUrl();
if (TextUtils.isEmpty(linkImageUrl)) {
binding.ivLinkPreview.setVisibility(View.GONE);
} else {
binding.ivLinkPreview.setImageURI(linkImageUrl);
}
if (TextUtils.isEmpty(linkContext.getLinkTitle())) {
binding.tvLinkTitle.setVisibility(View.GONE);
} else {
binding.tvLinkTitle.setText(linkContext.getLinkTitle());
}
if (TextUtils.isEmpty(linkContext.getLinkSummary())) {
binding.tvLinkSummary.setVisibility(View.GONE);
} else {
binding.tvLinkSummary.setText(linkContext.getLinkSummary());
}
binding.tvMessage.setText(TextUtils.getSpannableUrl(link.getText()));
}
}

View File

@ -1,64 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemMediaModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageMediaShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmMediaShareBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaShareBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
final DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final ProfileModel modelUser = mediaModel.getUser();
if (modelUser != null) {
binding.tvMessage.setText(HtmlCompat.fromHtml(
"<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>",
FROM_HTML_MODE_COMPACT));
}
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
}
}

View File

@ -1,52 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin());
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
}
}

View File

@ -1,30 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessagePlaceholderViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessagePlaceholderViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT));
}
}

View File

@ -1,35 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageProfileViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmProfileBinding binding;
public DirectMessageProfileViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmProfileBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
binding.btnOpenProfile.setOnClickListener(onClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final ProfileModel profileModel = directItemModel.getProfileModel();
if (profileModel == null) return;
binding.profileInfo.setImageURI(profileModel.getSdProfilePic());
binding.btnOpenProfile.setTag(profileModel);
binding.tvFullName.setText(profileModel.getName());
binding.profileInfoText.setText(profileModel.getUsername());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
}
}

View File

@ -1,104 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenExpiringMediaType;
import awais.instagrabber.models.enums.RavenMediaViewType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageRavenMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageRavenMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
binding.tvMessage.setVisibility(View.GONE);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
final DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel();
DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null ||
TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
DirectItemModel.RavenExpiringMediaActionSummaryModel mediaActionSummary = null;
if (ravenMediaModel != null) {
mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary();
}
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE);
int textRes = R.string.dms_inbox_raven_media_unknown;
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired;
if (!isExpired) {
if (mediaActionSummary != null) {
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType();
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED)
textRes = R.string.dms_inbox_raven_media_delivered;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT)
textRes = R.string.dms_inbox_raven_media_sent;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED)
textRes = R.string.dms_inbox_raven_media_opened;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED)
textRes = R.string.dms_inbox_raven_media_replayed;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING)
textRes = R.string.dms_inbox_raven_media_sending;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED)
textRes = R.string.dms_inbox_raven_media_blocked;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED)
textRes = R.string.dms_inbox_raven_media_suggested;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT)
textRes = R.string.dms_inbox_raven_media_screenshot;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER)
textRes = R.string.dms_inbox_raven_media_cant_deliver;
}
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType();
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) {
final MediaItemType mediaType = mediaModel.getMediaType();
textRes = -1;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
}
}
if (textRes != -1) {
binding.tvMessage.setText(context.getText(textRes));
binding.tvMessage.setVisibility(View.VISIBLE);
}
}
}

View File

@ -1,67 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageReelShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding binding,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
binding.tvMessage.setMentionClickListener(mentionClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare();
CharSequence text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.tvMessage.setVisibility(View.GONE);
} else {
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
binding.tvMessage.setText(text);
}
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
final MediaItemType mediaType = reelShareMedia.getMediaType();
if (mediaType == null) {
binding.mediaExpiredIcon.setVisibility(View.VISIBLE);
} else {
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
reelShareMedia.getHeight(),
reelShareMedia.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}

View File

@ -1,67 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageStoryShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmStoryShareBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageStoryShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmStoryShareBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
binding.tvMessage.setVisibility(View.GONE);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare();
if (reelShare == null) {
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT));
binding.tvMessage.setVisibility(View.VISIBLE);
} else {
final String text = reelShare.getText();
if (!TextUtils.isEmpty(text)) {
binding.tvMessage.setText(text);
binding.tvMessage.setVisibility(View.VISIBLE);
} else {
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
final MediaItemType mediaType = reelShareMedia.getMediaType();
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
reelShareMedia.getHeight(),
reelShareMedia.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}
}

View File

@ -1,43 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.text.Spanned;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageTextViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageTextViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
this.binding.tvMessage.setMentionClickListener(mentionClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
CharSequence text = directItemModel.getText();
text = TextUtils.getSpannableUrl(text.toString()); // for urls
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
if (text instanceof Spanned)
binding.tvMessage.setText(text, TextView.BufferType.SPANNABLE);
else if (text == "") {
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
} else binding.tvMessage.setText(text);
}
}

View File

@ -1,29 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageVideoCallEventViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageVideoCallEventViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
// todo add call event info
binding.tvMessage.setVisibility(View.VISIBLE);
binding.tvMessage.setBackgroundColor(0xFF_1F90E6);
}
}

View File

@ -1,77 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.NumberUtils;
public class DirectMessageVoiceMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmVoiceMediaBinding binding;
private DirectItemModel.DirectItemVoiceMediaModel prevVoiceModel;
private ImageView prevPlayIcon;
public DirectMessageVoiceMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmVoiceMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
// todo pause / resume
// todo release prev audio, start new voice
binding.btnPlayVoice.setOnClickListener(v -> {
final Object tag = v.getTag();
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0);
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = (DirectItemModel.DirectItemVoiceMediaModel) tag;
final boolean voicePlaying = voiceMediaModel.isPlaying();
voiceMediaModel.setPlaying(!voicePlaying);
if (voiceMediaModel == prevVoiceModel) {
// todo pause / resume
} else {
// todo release prev audio, start new voice
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false);
if (prevPlayIcon != null)
prevPlayIcon.setImageResource(android.R.drawable.ic_media_play);
}
if (voicePlaying) {
playIcon.setImageResource(android.R.drawable.ic_media_play);
} else {
playIcon.setImageResource(android.R.drawable.ic_media_pause);
}
prevVoiceModel = voiceMediaModel;
prevPlayIcon = playIcon;
});
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel();
if (voiceMediaModel != null) {
final int[] waveformData = voiceMediaModel.getWaveformData();
if (waveformData != null) binding.waveformSeekBar.setSample(waveformData);
final long durationMs = voiceMediaModel.getDurationMs();
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs));
binding.waveformSeekBar.setProgress(voiceMediaModel.getProgress());
binding.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> {
// todo progress audio player
voiceMediaModel.setProgress(progress);
if (fromUser)
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs * progress / 100));
});
binding.btnPlayVoice.setTag(voiceMediaModel);
} else {
binding.waveformSeekBar.setProgress(0);
}
}
}

View File

@ -0,0 +1,89 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
import awais.instagrabber.customviews.VerticalImageSpan;
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public class DirectPendingUserViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = DirectPendingUserViewHolder.class.getSimpleName();
private final LayoutDmPendingUserItemBinding binding;
private final PendingUserCallback callback;
private final int drawableSize;
private VerticalImageSpan verifiedSpan;
public DirectPendingUserViewHolder(@NonNull final LayoutDmPendingUserItemBinding binding,
final PendingUserCallback callback) {
super(binding.getRoot());
this.binding = binding;
this.callback = callback;
drawableSize = Utils.convertDpToPx(24);
}
public void bind(final int position, final PendingUser pendingUser) {
if (pendingUser == null) return;
binding.getRoot().setOnClickListener(v -> {
if (callback == null) return;
callback.onClick(position, pendingUser);
});
setUsername(pendingUser);
binding.requester.setText(itemView.getResources().getString(R.string.added_by, pendingUser.getRequester()));
binding.profilePic.setImageURI(pendingUser.getUser().getProfilePicUrl());
if (pendingUser.isInProgress()) {
binding.approve.setVisibility(View.GONE);
binding.deny.setVisibility(View.GONE);
binding.progress.setVisibility(View.VISIBLE);
return;
}
binding.approve.setVisibility(View.VISIBLE);
binding.deny.setVisibility(View.VISIBLE);
binding.progress.setVisibility(View.GONE);
binding.approve.setOnClickListener(v -> {
if (callback == null) return;
callback.onApprove(position, pendingUser);
});
binding.deny.setOnClickListener(v -> {
if (callback == null) return;
callback.onDeny(position, pendingUser);
});
}
private void setUsername(final PendingUser pendingUser) {
final User user = pendingUser.getUser();
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());
if (user.isVerified()) {
if (verifiedSpan == null) {
final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
if (verifiedDrawable != null) {
final Drawable drawable = verifiedDrawable.mutate();
drawable.setBounds(0, 0, drawableSize, drawableSize);
verifiedSpan = new VerticalImageSpan(drawable);
}
}
try {
if (verifiedSpan != null) {
sb.append(" ");
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
Log.e(TAG, "bind: ", e);
}
}
binding.username.setText(sb);
}
}

View File

@ -0,0 +1,71 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.utils.emoji.EmojiParser;
public class DirectReactionViewHolder extends RecyclerView.ViewHolder {
private final LayoutDmUserItemBinding binding;
private final long viewerId;
private final String itemId;
private final OnReactionClickListener onReactionClickListener;
private final EmojiParser emojiParser;
public DirectReactionViewHolder(final LayoutDmUserItemBinding binding,
final long viewerId,
final String itemId,
final OnReactionClickListener onReactionClickListener) {
super(binding.getRoot());
this.binding = binding;
this.viewerId = viewerId;
this.itemId = itemId;
this.onReactionClickListener = onReactionClickListener;
binding.info.setVisibility(View.GONE);
binding.secondaryImage.setVisibility(View.VISIBLE);
emojiParser = EmojiParser.getInstance();
}
public void bind(final DirectItemEmojiReaction reaction,
@Nullable final User user) {
itemView.setOnClickListener(v -> {
if (onReactionClickListener == null) return;
onReactionClickListener.onReactionClick(itemId, reaction);
});
setUser(user);
setReaction(reaction);
}
private void setReaction(final DirectItemEmojiReaction reaction) {
final Emoji emoji = emojiParser.getEmoji(reaction.getEmoji());
if (emoji == null) {
binding.secondaryImage.setImageDrawable(null);
return;
}
binding.secondaryImage.setImageDrawable(emoji.getDrawable());
}
private void setUser(final User user) {
if (user == null) {
binding.fullName.setText("");
binding.username.setText("");
binding.profilePic.setImageURI((String) null);
return;
}
binding.fullName.setText(user.getFullName());
if (user.getPk() == viewerId) {
binding.username.setText(R.string.tap_to_remove);
} else {
binding.username.setText(user.getUsername());
}
binding.profilePic.setImageURI(user.getProfilePicUrl());
}
}

View File

@ -0,0 +1,102 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserLongClickListener;
import awais.instagrabber.customviews.VerticalImageSpan;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public class DirectUserViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = DirectUserViewHolder.class.getSimpleName();
private final LayoutDmUserItemBinding binding;
private final OnDirectUserClickListener onClickListener;
private final OnDirectUserLongClickListener onLongClickListener;
private final int drawableSize;
private VerticalImageSpan verifiedSpan;
public DirectUserViewHolder(@NonNull final LayoutDmUserItemBinding binding,
final OnDirectUserClickListener onClickListener,
final OnDirectUserLongClickListener onLongClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onClickListener = onClickListener;
this.onLongClickListener = onLongClickListener;
drawableSize = Utils.convertDpToPx(24);
}
public void bind(final int position,
final User user,
final boolean isAdmin,
final boolean isInviter,
final boolean showSelection,
final boolean isSelected) {
if (user == null) return;
binding.getRoot().setOnClickListener(v -> {
if (onClickListener == null) return;
onClickListener.onClick(position, user, isSelected);
});
binding.getRoot().setOnLongClickListener(v -> {
if (onLongClickListener == null) return false;
return onLongClickListener.onLongClick(position, user);
});
setFullName(user);
binding.username.setText(user.getUsername());
binding.profilePic.setImageURI(user.getProfilePicUrl());
setInfo(isAdmin, isInviter);
setSelection(showSelection, isSelected);
}
private void setFullName(final User user) {
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getFullName());
if (user.isVerified()) {
if (verifiedSpan == null) {
final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
if (verifiedDrawable != null) {
final Drawable drawable = verifiedDrawable.mutate();
drawable.setBounds(0, 0, drawableSize, drawableSize);
verifiedSpan = new VerticalImageSpan(drawable);
}
}
try {
if (verifiedSpan != null) {
sb.append(" ");
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
Log.e(TAG, "bind: ", e);
}
}
binding.fullName.setText(sb);
}
private void setInfo(final boolean isAdmin, final boolean isInviter) {
if (!isAdmin && !isInviter) {
binding.info.setVisibility(View.GONE);
return;
}
if (isAdmin) {
binding.info.setText(R.string.admin);
return;
}
binding.info.setText(R.string.inviter);
}
private void setSelection(final boolean showSelection, final boolean isSelected) {
binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);
binding.getRoot().setSelected(isSelected);
}
}

View File

@ -0,0 +1,89 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.UserSearchResultsAdapter.OnRecipientClickListener;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
public class RecipientThreadViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = RecipientThreadViewHolder.class.getSimpleName();
private final LayoutDmUserItemBinding binding;
private final OnRecipientClickListener onThreadClickListener;
private final float translateAmount;
public RecipientThreadViewHolder(@NonNull final LayoutDmUserItemBinding binding,
final OnRecipientClickListener onThreadClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onThreadClickListener = onThreadClickListener;
binding.info.setVisibility(View.GONE);
final Resources resources = itemView.getResources();
final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_inbox_avatar_size);
translateAmount = ((float) avatarSize) / 7;
}
public void bind(final int position,
final DirectThread thread,
final boolean showSelection,
final boolean isSelected) {
if (thread == null) return;
binding.getRoot().setOnClickListener(v -> {
if (onThreadClickListener == null) return;
onThreadClickListener.onClick(position, RankedRecipient.of(thread), isSelected);
});
binding.fullName.setText(thread.getThreadTitle());
setUsername(thread);
setProfilePic(thread);
setSelection(showSelection, isSelected);
}
private void setProfilePic(final DirectThread thread) {
final List<User> users = thread.getUsers();
binding.profilePic.setImageURI(users.get(0).getProfilePicUrl());
binding.profilePic.setScaleX(1);
binding.profilePic.setScaleY(1);
binding.profilePic.setTranslationX(0);
binding.profilePic.setTranslationY(0);
if (users.size() > 1) {
binding.profilePic2.setVisibility(View.VISIBLE);
binding.profilePic2.setImageURI(users.get(1).getProfilePicUrl());
binding.profilePic2.setTranslationX(translateAmount);
binding.profilePic2.setTranslationY(translateAmount);
final float scaleAmount = 0.75f;
binding.profilePic2.setScaleX(scaleAmount);
binding.profilePic2.setScaleY(scaleAmount);
binding.profilePic.setScaleX(scaleAmount);
binding.profilePic.setScaleY(scaleAmount);
binding.profilePic.setTranslationX(-translateAmount);
binding.profilePic.setTranslationY(-translateAmount);
return;
}
binding.profilePic2.setVisibility(View.GONE);
}
private void setUsername(final DirectThread thread) {
if (thread.isGroup()) {
binding.username.setVisibility(View.GONE);
return;
}
binding.username.setVisibility(View.VISIBLE);
// for a non-group thread, the thread title is the username so set the full name in the username text view
binding.username.setText(thread.getUsers().get(0).getFullName());
}
private void setSelection(final boolean showSelection, final boolean isSelected) {
binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);
binding.getRoot().setSelected(isSelected);
}
}

View File

@ -11,9 +11,11 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedBottomBinding; import awais.instagrabber.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding; import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import static android.text.TextUtils.TruncateAt.END; import static android.text.TextUtils.TruncateAt.END;
@ -35,52 +37,60 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
this.feedItemCallback = feedItemCallback; this.feedItemCallback = feedItemCallback;
} }
public void bind(final FeedModel feedModel) { public void bind(final Media media) {
if (feedModel == null) { if (media == null) {
return; return;
} }
setupProfilePic(feedModel); setupProfilePic(media);
setupLocation(feedModel); setupLocation(media);
bottomBinding.tvPostDate.setText(feedModel.getPostDate()); bottomBinding.tvPostDate.setText(media.getDate());
setupComments(feedModel); setupComments(media);
setupCaption(feedModel); setupCaption(media);
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER) { if (media.getMediaType() != MediaItemType.MEDIA_TYPE_SLIDER) {
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, -1)); bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(media, -1));
} }
bindItem(feedModel); bindItem(media);
} }
private void setupComments(final FeedModel feedModel) { private void setupComments(@NonNull final Media feedModel) {
final long commentsCount = feedModel.getCommentsCount(); final long commentsCount = feedModel.getCommentCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount)); bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel)); bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
} }
private void setupProfilePic(final FeedModel feedModel) { private void setupProfilePic(@NonNull final Media media) {
final ProfileModel profileModel = feedModel.getProfileModel(); final User user = media.getUser();
if (profileModel != null) { if (user == null) {
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic)); topBinding.ivProfilePic.setVisibility(View.GONE);
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic()); topBinding.title.setVisibility(View.GONE);
setupTitle(feedModel); return;
} }
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(media, topBinding.ivProfilePic));
topBinding.ivProfilePic.setImageURI(user.getProfilePicUrl());
setupTitle(media);
} }
private void setupTitle(final FeedModel feedModel) { private void setupTitle(@NonNull final Media media) {
// final int titleLen = profileModel.getUsername().length() + 1; // final int titleLen = profileModel.getUsername().length() + 1;
// final SpannableString spannableString = new SpannableString(); // final SpannableString spannableString = new SpannableString();
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0); // spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
final ProfileModel profileModel = feedModel.getProfileModel(); final User user = media.getUser();
final String title = "@" + profileModel.getUsername(); final String title = "@" + user.getUsername();
topBinding.title.setText(title); topBinding.title.setText(title);
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(feedModel, topBinding.ivProfilePic)); topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(media, topBinding.ivProfilePic));
} }
private void setupCaption(final FeedModel feedModel) { private void setupCaption(final Media media) {
bottomBinding.viewerCaption.clearOnMentionClickListeners(); bottomBinding.viewerCaption.clearOnMentionClickListeners();
bottomBinding.viewerCaption.clearOnHashtagClickListeners(); bottomBinding.viewerCaption.clearOnHashtagClickListeners();
bottomBinding.viewerCaption.clearOnURLClickListeners(); bottomBinding.viewerCaption.clearOnURLClickListeners();
bottomBinding.viewerCaption.clearOnEmailClickListeners(); bottomBinding.viewerCaption.clearOnEmailClickListeners();
final CharSequence postCaption = feedModel.getPostCaption(); final Caption caption = media.getCaption();
if (caption == null) {
bottomBinding.viewerCaption.setVisibility(View.GONE);
return;
}
final CharSequence postCaption = caption.getText();
final boolean captionEmpty = TextUtils.isEmpty(postCaption); final boolean captionEmpty = TextUtils.isEmpty(postCaption);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE); bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (captionEmpty) return; if (captionEmpty) return;
@ -103,22 +113,31 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText())); bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
} }
private void setupLocation(final FeedModel feedModel) { private void setupLocation(@NonNull final Media media) {
final String locationName = feedModel.getLocationName(); final Location location = media.getLocation();
if (TextUtils.isEmpty(locationName)) { if (location == null) {
topBinding.location.setVisibility(View.GONE); topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams( topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
)); ));
} else { }
topBinding.location.setVisibility(View.VISIBLE); else {
topBinding.location.setText(locationName); final String locationName = location.getName();
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams( if (TextUtils.isEmpty(locationName)) {
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT topBinding.location.setVisibility(View.GONE);
)); topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(feedModel)); RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
));
} else {
topBinding.location.setVisibility(View.VISIBLE);
topBinding.location.setText(locationName);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(media));
}
} }
} }
public abstract void bindItem(final FeedModel feedModel); public abstract void bindItem(final Media media);
} }

View File

@ -16,7 +16,8 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedPhotoBinding; import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
public class FeedPhotoViewHolder extends FeedItemViewHolder { public class FeedPhotoViewHolder extends FeedItemViewHolder {
@ -30,7 +31,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback); super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding; this.binding = binding;
this.feedItemCallback = feedItemCallback; this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE); binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE); // binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false); binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources()) final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
@ -40,14 +41,12 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
} }
@Override @Override
public void bindItem(final FeedModel feedModel) { public void bindItem(final Media media) {
if (feedModel == null) { if (media == null) return;
return;
}
binding.getRoot().post(() -> { binding.getRoot().post(() -> {
setDimensions(feedModel); setDimensions(media);
final String thumbnailUrl = feedModel.getThumbnailUrl(); final String thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
String url = feedModel.getDisplayUrl(); String url = ResponseBodyUtils.getImageUrl(media);
if (TextUtils.isEmpty(url)) url = thumbnailUrl; if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
// .setLocalThumbnailPreviewsEnabled(true) // .setLocalThumbnailPreviewsEnabled(true)
@ -62,7 +61,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
@Override @Override
public boolean onSingleTapConfirmed(final MotionEvent e) { public boolean onSingleTapConfirmed(final MotionEvent e) {
if (feedItemCallback != null) { if (feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer); feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true; return true;
} }
return false; return false;
@ -71,8 +70,8 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
}); });
} }
private void setDimensions(final FeedModel feedModel) { private void setDimensions(final Media feedModel) {
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight(); final float aspectRatio = (float) feedModel.getOriginalWidth() / feedModel.getOriginalHeight();
binding.imageViewer.setAspectRatio(aspectRatio); binding.imageViewer.setAspectRatio(aspectRatio);
} }
} }

View File

@ -13,8 +13,7 @@ import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.SliderCallbackAdapter; import awais.instagrabber.adapters.SliderCallbackAdapter;
import awais.instagrabber.adapters.SliderItemsAdapter; import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.databinding.ItemFeedSliderBinding; import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
@ -30,7 +29,7 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback); super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding; this.binding = binding;
this.feedItemCallback = feedItemCallback; this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE); binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE); // binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
layoutParams.height = Utils.displayMetrics.widthPixels + 1; layoutParams.height = Utils.displayMetrics.widthPixels + 1;
@ -39,8 +38,8 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
} }
@Override @Override
public void bindItem(final FeedModel feedModel) { public void bindItem(final Media feedModel) {
final List<PostChild> sliderItems = feedModel.getSliderItems(); final List<Media> sliderItems = feedModel.getCarouselMedia();
final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0; final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;
if (sliderItemLen <= 0) return; if (sliderItemLen <= 0) return;
final String text = "1/" + sliderItemLen; final String text = "1/" + sliderItemLen;
@ -68,7 +67,7 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
adapter.submitList(sliderItems); adapter.submitList(sliderItems);
} }
private void setDimensions(final View view, final PostChild model) { private void setDimensions(final View view, final Media model) {
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
int requiredWidth = layoutParams.width; int requiredWidth = layoutParams.width;
if (requiredWidth <= 0) { if (requiredWidth <= 0) {
@ -86,10 +85,10 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
setLayoutParamDimens(binding.mediaList, model); setLayoutParamDimens(binding.mediaList, model);
} }
private void setLayoutParamDimens(final View view, final PostChild model) { private void setLayoutParamDimens(final View view, final Media model) {
final int requiredWidth = view.getMeasuredWidth(); final int requiredWidth = view.getMeasuredWidth();
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth()); final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getOriginalHeight(), model.getOriginalWidth());
layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight; layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight;
view.requestLayout(); view.requestLayout();
} }

View File

@ -12,13 +12,17 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import java.util.List;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter; import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper; import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.databinding.ItemFeedVideoBinding; import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -32,7 +36,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private final DefaultDataSourceFactory dataSourceFactory; private final DefaultDataSourceFactory dataSourceFactory;
private CacheDataSourceFactory cacheDataSourceFactory; private CacheDataSourceFactory cacheDataSourceFactory;
private FeedModel feedModel; private Media media;
// private final Runnable loadRunnable = new Runnable() { // private final Runnable loadRunnable = new Runnable() {
// @Override // @Override
@ -46,7 +50,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback); super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding; this.binding = binding;
this.feedItemCallback = feedItemCallback; this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.VISIBLE); binding.itemFeedBottom.btnViews.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper()); handler = new Handler(Looper.getMainLooper());
final Context context = binding.getRoot().getContext(); final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram"); dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
@ -57,68 +61,55 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
} }
@Override @Override
public void bindItem(final FeedModel feedModel) { public void bindItem(final Media media) {
// Log.d(TAG, "Binding post: " + feedModel.getPostId()); // Log.d(TAG, "Binding post: " + feedModel.getPostId());
this.feedModel = feedModel; this.media = media;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount())); binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(media.getViewCount()));
// showOrHideDetails(false);
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f; final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() { final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
@Override @Override
public void onThumbnailClick() { public void onThumbnailClick() {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail); feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
} }
@Override @Override
public void onPlayerViewLoaded() { public void onPlayerViewLoaded() {
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels; final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth()); final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
layoutParams.width = requiredWidth; layoutParams.width = requiredWidth;
layoutParams.height = resultingHeight; layoutParams.height = resultingHeight;
binding.videoPost.playerView.requestLayout(); binding.videoPost.playerView.requestLayout();
setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
} }
}; };
// final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory; final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
// final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory); String videoUrl = null;
// final Uri uri = Uri.parse(feedModel.getDisplayUrl()); final List<VideoVersion> videoVersions = media.getVideoVersions();
// final MediaItem mediaItem = MediaItem.fromUri(uri); if (videoVersions != null && !videoVersions.isEmpty()) {
// final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem); final VideoVersion videoVersion = videoVersions.get(0);
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight(); videoUrl = videoVersion.getUrl();
}
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(), final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
binding.videoPost, binding.videoPost,
feedModel.getDisplayUrl(), videoUrl,
vol, vol,
aspectRatio, aspectRatio,
feedModel.getThumbnailUrl(), ResponseBodyUtils.getThumbUrl(media),
false, false,
null, null,
videoPlayerCallback); videoPlayerCallback);
binding.videoPost.thumbnail.post(() -> { binding.videoPost.thumbnail.post(() -> {
if (feedModel.getImageHeight() > 0.8 * Utils.displayMetrics.heightPixels) { if (media.getOriginalHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams();
layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels); layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
binding.videoPost.thumbnail.requestLayout(); binding.videoPost.thumbnail.requestLayout();
} }
}); });
// binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
// final float newVol = videoPlayerViewHelper.toggleMute();
// setMuteIcon(newVol);
// Utils.sessionVolumeFull = newVol == 1f;
// });
// binding.videoPost.playerView.setOnClickListener(v -> videoPlayerViewHelper.togglePlayback());
} }
public Media getCurrentFeedModel() {
private void setMuteIcon(final float vol) { return media;
// binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
}
public FeedModel getCurrentFeedModel() {
return feedModel;
} }
// public void stopPlaying() { // public void stopPlaying() {

View File

@ -0,0 +1,74 @@
package awais.instagrabber.animations;
import android.graphics.PointF;
import android.view.animation.Interpolator;
public class CubicBezierInterpolator implements Interpolator {
public static final CubicBezierInterpolator DEFAULT = new CubicBezierInterpolator(0.25, 0.1, 0.25, 1);
public static final CubicBezierInterpolator EASE_OUT = new CubicBezierInterpolator(0, 0, .58, 1);
public static final CubicBezierInterpolator EASE_OUT_QUINT = new CubicBezierInterpolator(.23, 1, .32, 1);
public static final CubicBezierInterpolator EASE_IN = new CubicBezierInterpolator(.42, 0, 1, 1);
public static final CubicBezierInterpolator EASE_BOTH = new CubicBezierInterpolator(.42, 0, .58, 1);
protected PointF start;
protected PointF end;
protected PointF a = new PointF();
protected PointF b = new PointF();
protected PointF c = new PointF();
public CubicBezierInterpolator(PointF start, PointF end) throws IllegalArgumentException {
if (start.x < 0 || start.x > 1) {
throw new IllegalArgumentException("startX value must be in the range [0, 1]");
}
if (end.x < 0 || end.x > 1) {
throw new IllegalArgumentException("endX value must be in the range [0, 1]");
}
this.start = start;
this.end = end;
}
public CubicBezierInterpolator(float startX, float startY, float endX, float endY) {
this(new PointF(startX, startY), new PointF(endX, endY));
}
public CubicBezierInterpolator(double startX, double startY, double endX, double endY) {
this((float) startX, (float) startY, (float) endX, (float) endY);
}
@Override
public float getInterpolation(float time) {
return getBezierCoordinateY(getXForTime(time));
}
protected float getBezierCoordinateY(float time) {
c.y = 3 * start.y;
b.y = 3 * (end.y - start.y) - c.y;
a.y = 1 - c.y - b.y;
return time * (c.y + time * (b.y + time * a.y));
}
protected float getXForTime(float time) {
float x = time;
float z;
for (int i = 1; i < 14; i++) {
z = getBezierCoordinateX(x) - time;
if (Math.abs(z) < 1e-3) {
break;
}
x -= z / getXDerivate(x);
}
return x;
}
private float getXDerivate(float t) {
return c.x + t * (2 * b.x + 3 * a.x * t);
}
private float getBezierCoordinateX(float time) {
c.x = 3 * start.x;
b.x = 3 * (end.x - start.x) - c.x;
a.x = 1 - c.x - b.x;
return time * (c.x + time * (b.x + time * a.x));
}
}

View File

@ -0,0 +1,61 @@
package awais.instagrabber.animations;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;
// https://medium.com/better-programming/animated-fab-button-with-more-options-2dcf7118fff6
public class FabAnimation {
public static boolean rotateFab(final View v, boolean rotate) {
v.animate().setDuration(200)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
})
.rotation(rotate ? 135f : 0f);
return rotate;
}
public static void showIn(final View v) {
v.setVisibility(View.VISIBLE);
v.setAlpha(0f);
v.setTranslationY(v.getHeight());
v.animate()
.setDuration(200)
.translationY(0)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
})
.alpha(1f)
.start();
}
public static void showOut(final View v) {
v.setVisibility(View.VISIBLE);
v.setAlpha(1f);
v.setTranslationY(0);
v.animate()
.setDuration(200)
.translationY(v.getHeight())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
v.setVisibility(View.GONE);
super.onAnimationEnd(animation);
}
}).alpha(0f)
.start();
}
public static void init(final View v) {
v.setVisibility(View.GONE);
v.setTranslationY(v.getHeight());
v.setAlpha(0f);
}
}

View File

@ -0,0 +1,84 @@
package awais.instagrabber.animations;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
/**
* A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
* This class should be extended so that subclasses can define the reveal shape as the
* animation progresses from 0 to 1.
*/
public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
protected Rect mOutline;
protected float mOutlineRadius;
public RevealOutlineAnimation() {
mOutline = new Rect();
}
/**
* Returns whether elevation should be removed for the duration of the reveal animation.
*/
abstract boolean shouldRemoveElevationDuringAnimation();
/**
* Sets the progress, from 0 to 1, of the reveal animation.
*/
abstract void setProgress(float progress);
public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
ValueAnimator va =
isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
private boolean mIsClippedToOutline;
private ViewOutlineProvider mOldOutlineProvider;
public void onAnimationStart(Animator animation) {
mIsClippedToOutline = revealView.getClipToOutline();
mOldOutlineProvider = revealView.getOutlineProvider();
revealView.setOutlineProvider(RevealOutlineAnimation.this);
revealView.setClipToOutline(true);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(-elevation);
}
}
public void onAnimationEnd(Animator animation) {
revealView.setOutlineProvider(mOldOutlineProvider);
revealView.setClipToOutline(mIsClippedToOutline);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(0);
}
}
});
va.addUpdateListener(v -> {
float progress = (Float) v.getAnimatedValue();
setProgress(progress);
revealView.invalidateOutline();
});
return va;
}
@Override
public void getOutline(View v, Outline outline) {
outline.setRoundRect(mOutline, mOutlineRadius);
}
public float getRadius() {
return mOutlineRadius;
}
public void getOutline(Rect out) {
out.set(mOutline);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package awais.instagrabber.animations;
import android.graphics.Rect;
/**
* A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii
* and two {@link Rect}s.
* <p>
* An example usage of this provider is an outline that starts out as a circle and ends
* as a rounded rectangle.
*/
public class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {
private final float mStartRadius;
private final float mEndRadius;
private final Rect mStartRect;
private final Rect mEndRect;
public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect, Rect endRect) {
mStartRadius = startRadius;
mEndRadius = endRadius;
mStartRect = startRect;
mEndRect = endRect;
}
@Override
public boolean shouldRemoveElevationDuringAnimation() {
return false;
}
@Override
public void setProgress(float progress) {
mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
}
}

View File

@ -0,0 +1,45 @@
package awais.instagrabber.animations;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
public class ScaleAnimation {
private final View view;
public ScaleAnimation(View view) {
this.view = view;
}
public void start() {
AnimatorSet set = new AnimatorSet();
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 2.0f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 2.0f);
set.setDuration(150);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.playTogether(scaleY, scaleX);
set.start();
}
public void stop() {
AnimatorSet set = new AnimatorSet();
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f);
// scaleY.setDuration(250);
// scaleY.setInterpolator(new DecelerateInterpolator());
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f);
// scaleX.setDuration(250);
// scaleX.setInterpolator(new DecelerateInterpolator());
set.setDuration(150);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.playTogether(scaleY, scaleX);
set.start();
}
}

View File

@ -17,13 +17,14 @@ import java.util.List;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel; import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector; //import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector; //import static awais.instagrabber.utils.Utils.logCollector;
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> { public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
private static final String TAG = "CommentsFetcher"; private static final String TAG = "CommentsFetcher";
@ -106,44 +107,34 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
if (childComment != null) { if (childComment != null) {
final JSONObject owner = childComment.getJSONObject("owner"); final JSONObject owner = childComment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false, final User user = new User(
false, owner.optLong(Constants.EXTRAS_ID, 0),
false, owner.getString(Constants.EXTRAS_USERNAME),
owner.getString(Constants.EXTRAS_ID), null,
owner.getString(Constants.EXTRAS_USERNAME), false,
null, owner.getString("profile_pic_url"),
null, null,
null, new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
owner.getString("profile_pic_url"), false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null,
null, null, null, null);
0,
0,
0,
false,
false,
false,
false,
false);
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by"); final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID), commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"), childComment.getString("text"),
childComment.getLong("created_at"), childComment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0, likedBy != null ? likedBy.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"), childComment.getBoolean("viewer_has_liked"),
profileModel)); user));
} }
} }
} }
} }
conn.disconnect(); conn.disconnect();
} catch (final Exception e) { } catch (final Exception e) {
if (logCollector != null) // if (logCollector != null)
logCollector.appendException(e, // logCollector.appendException(e,
LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, // LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
"getChildComments", // "getChildComments",
new Pair<>("commentModels.size", commentModels.size())); // new Pair<>("commentModels.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e(TAG, "", e); if (BuildConfig.DEBUG) Log.e(TAG, "", e);
if (fetchListener != null) fetchListener.onFailure(e); if (fetchListener != null) fetchListener.onFailure(e);
break; break;
@ -156,138 +147,122 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull @NonNull
private synchronized List<CommentModel> getParentComments() { private synchronized List<CommentModel> getParentComments() {
final List<CommentModel> commentModels = new ArrayList<>(); final List<CommentModel> commentModels = new ArrayList<>();
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" + final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}"; "{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
try { try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false); conn.setUseCaches(false);
conn.connect(); conn.connect();
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null; if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else { else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data") final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media") .getJSONObject("shortcode_media")
.getJSONObject( .getJSONObject(
"edge_media_to_parent_comment"); "edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info"); final JSONObject pageInfo = parentComments.getJSONObject("page_info");
final String foundEndCursor = pageInfo.optString("end_cursor"); final String foundEndCursor = pageInfo.optString("end_cursor");
final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor)); final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor));
// final boolean containsToken = endCursor.contains("bifilter_token"); // final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) { // if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
// final JSONObject endCursorObject = new JSONObject(endCursor); // final JSONObject endCursorObject = new JSONObject(endCursor);
// endCursor = endCursorObject.optString("cached_comments_cursor"); // endCursor = endCursorObject.optString("cached_comments_cursor");
// //
// if (!Utils.isEmpty(endCursor)) // if (!Utils.isEmpty(endCursor))
// endCursor = "{\\\"cached_comments_cursor\\\": \\\"" + endCursor + "\\\", "; // endCursor = "{\\\"cached_comments_cursor\\\": \\\"" + endCursor + "\\\", ";
// else // else
// endCursor = "{"; // endCursor = "{";
// //
// endCursor = endCursor + "\\\"bifilter_token\\\": \\\"" + endCursorObject.getString("bifilter_token") + "\\\"}"; // endCursor = endCursor + "\\\"bifilter_token\\\": \\\"" + endCursorObject.getString("bifilter_token") + "\\\"}";
// } // }
// else if (containsToken) endCursor = null; // else if (containsToken) endCursor = null;
final JSONArray comments = parentComments.getJSONArray("edges"); final JSONArray comments = parentComments.getJSONArray("edges");
final int commentsLen = comments.length(); final int commentsLen = comments.length();
for (int i = 0; i < commentsLen; ++i) { for (int i = 0; i < commentsLen; ++i) {
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node"); final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
final JSONObject owner = comment.getJSONObject("owner"); final JSONObject owner = comment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false, final User user = new User(
false, owner.optLong(Constants.EXTRAS_ID, 0),
owner.optBoolean("is_verified"), owner.getString(Constants.EXTRAS_USERNAME),
owner.getString(Constants.EXTRAS_ID), null,
owner.getString(Constants.EXTRAS_USERNAME), false,
null, owner.getString("profile_pic_url"),
null, null,
null, new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
owner.getString("profile_pic_url"), owner.optBoolean("is_verified"),
null, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null, null,
0, null, null);
0, final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
0, final String commentId = comment.getString(Constants.EXTRAS_ID);
false, final CommentModel commentModel = new CommentModel(commentId,
false, comment.getString("text"),
false, comment.getLong("created_at"),
false, likedBy != null ? likedBy.optLong("count", 0) : 0,
false); comment.getBoolean("viewer_has_liked"),
user);
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject;
final JSONArray childCommentsArray;
final int childCommentsLen;
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
(childCommentsArray = tempJsonObject.optJSONArray("edges")) != null
&& (childCommentsLen = childCommentsArray.length()) > 0) {
final JSONObject likedBy = comment.optJSONObject("edge_liked_by"); final String childEndCursor;
final String commentId = comment.getString(Constants.EXTRAS_ID); final boolean childHasNextPage;
final CommentModel commentModel = new CommentModel(commentId, if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
comment.getString("text"), childEndCursor = tempJsonObject.optString("end_cursor");
comment.getLong("created_at"), childHasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
likedBy != null ? likedBy.optLong("count", 0) : 0, } else {
comment.getBoolean("viewer_has_liked"), childEndCursor = null;
profileModel); childHasNextPage = false;
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject;
final JSONArray childCommentsArray;
final int childCommentsLen;
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
(childCommentsArray = tempJsonObject.optJSONArray("edges")) != null
&& (childCommentsLen = childCommentsArray.length()) > 0) {
final String childEndCursor;
final boolean childHasNextPage;
if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
childEndCursor = tempJsonObject.optString("end_cursor");
childHasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
} else {
childEndCursor = null;
childHasNextPage = false;
}
final List<CommentModel> childCommentModels = new ArrayList<>();
for (int j = 0; j < childCommentsLen; ++j) {
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
tempJsonObject = childComment.getJSONObject("owner");
final ProfileModel childProfileModel = new ProfileModel(false,
false,
tempJsonObject.optBoolean("is_verified"),
tempJsonObject.getString(Constants.EXTRAS_ID),
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
tempJsonObject.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
tempJsonObject = childComment.optJSONObject("edge_liked_by");
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
childProfileModel));
}
childCommentModels.get(childCommentsLen - 1).setPageCursor(childHasNextPage, childEndCursor);
commentModel.setChildCommentModels(childCommentModels);
} }
commentModels.add(commentModel);
}
}
conn.disconnect(); final List<CommentModel> childCommentModels = new ArrayList<>();
} catch (final Exception e) { for (int j = 0; j < childCommentsLen; ++j) {
if (logCollector != null) final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
new Pair<>("commentModelsList.size", commentModels.size())); tempJsonObject = childComment.getJSONObject("owner");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); final User childUser = new User(
if (fetchListener != null) fetchListener.onFailure(e); tempJsonObject.optLong(Constants.EXTRAS_ID, 0),
return null; tempJsonObject.getString(Constants.EXTRAS_USERNAME),
null,
false,
tempJsonObject.getString("profile_pic_url"),
null,
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
tempJsonObject.optBoolean("is_verified"), false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0,
null, null, null, null, null, null);
tempJsonObject = childComment.optJSONObject("edge_liked_by");
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
childUser));
}
childCommentModels.get(childCommentsLen - 1).setPageCursor(childHasNextPage, childEndCursor);
commentModel.setChildCommentModels(childCommentModels);
}
commentModels.add(commentModel);
}
} }
conn.disconnect();
} catch (final Exception e) {
// if (logCollector != null)
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
// new Pair<>("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (fetchListener != null) fetchListener.onFailure(e);
return null;
}
return commentModels; return commentModels;
} }
} }

View File

@ -0,0 +1,80 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.util.Collections;
import java.util.Locale;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.DirectMessagesService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class CreateThreadAction extends AsyncTask<Void, Void, Void> {
private static final String TAG = "CommentAction";
private final String cookie;
private final long userId;
private final OnTaskCompleteListener onTaskCompleteListener;
private final DirectMessagesService directMessagesService;
public CreateThreadAction(final String cookie, final long userId, final OnTaskCompleteListener onTaskCompleteListener) {
this.cookie = cookie;
this.userId = userId;
this.onTaskCompleteListener = onTaskCompleteListener;
directMessagesService = DirectMessagesService.getInstance(CookieUtils.getCsrfTokenFromCookie(cookie),
CookieUtils.getUserIdFromCookie(cookie),
Utils.settingsHelper.getString(Constants.DEVICE_UUID));
}
protected Void doInBackground(Void... lmao) {
final Call<DirectThread> createThreadRequest = directMessagesService.createThread(Collections.singletonList(userId), null);
createThreadRequest.enqueue(new Callback<DirectThread>() {
@Override
public void onResponse(@NonNull final Call<DirectThread> call, @NonNull final Response<DirectThread> response) {
if (!response.isSuccessful()) {
if (response.errorBody() != null) {
try {
final String string = response.errorBody().string();
final String msg = String.format(Locale.US,
"onResponse: url: %s, responseCode: %d, errorBody: %s",
call.request().url().toString(),
response.code(),
string);
Log.e(TAG, msg);
} catch (IOException e) {
Log.e(TAG, "onResponse: ", e);
}
}
Log.e(TAG, "onResponse: request was not successful and response error body was null");
}
onTaskCompleteListener.onTaskComplete(response.body());
if (response.body() == null) {
Log.e(TAG, "onResponse: thread is null");
}
}
@Override
public void onFailure(@NonNull final Call<DirectThread> call, @NonNull final Throwable t) {
onTaskCompleteListener.onTaskComplete(null);
}
});
return null;
}
// @Override
// protected void onPostExecute() {
// }
public interface OnTaskCompleteListener {
void onTaskComplete(final DirectThread thread);
}
}

View File

@ -1,10 +1,15 @@
package awais.instagrabber.asyncs; package awais.instagrabber.asyncs;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;
import awais.instagrabber.repositories.responses.WrappedMedia;
import awais.instagrabber.webservices.DiscoverService; import awais.instagrabber.webservices.DiscoverService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
@ -20,18 +25,28 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
} }
@Override @Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) { public void fetch(final FetchListener<List<Media>> fetchListener) {
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<DiscoverService.TopicalExploreResponse>() { discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<TopicalExploreFeedResponse>() {
@Override @Override
public void onSuccess(final DiscoverService.TopicalExploreResponse result) { public void onSuccess(final TopicalExploreFeedResponse result) {
if (result == null) { if (result == null) {
onFailure(new RuntimeException("result is null")); onFailure(new RuntimeException("result is null"));
return; return;
} }
moreAvailable = result.isMoreAvailable(); moreAvailable = result.isMoreAvailable();
topicalExploreRequest.setMaxId(result.getNextMaxId()); topicalExploreRequest.setMaxId(result.getNextMaxId());
final List<WrappedMedia> items = result.getItems();
final List<Media> posts;
if (items == null) {
posts = Collections.emptyList();
} else {
posts = items.stream()
.map(WrappedMedia::getMedia)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
if (fetchListener != null) { if (fetchListener != null) {
fetchListener.onResult(result.getItems()); fetchListener.onResult(posts);
} }
} }
@ -46,7 +61,7 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
@Override @Override
public void reset() { public void reset() {
topicalExploreRequest.setMaxId(-1); topicalExploreRequest.setMaxId(null);
} }
@Override @Override

Some files were not shown because too many files have changed in this diff Show More