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"
]
},
{
"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",
"name": "AWAiS",
@ -55,7 +73,7 @@
"login": "snajdovski",
"name": "Stefan Najdovski",
"avatar_url": "https://avatars2.githubusercontent.com/u/42580385?v=4",
"profile": "https://stefannajdovski.com/",
"profile": "https://snajdovski.github.io",
"contributions": [
"design",
"translation"
@ -91,16 +109,6 @@
"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",
"name": "Ricardo",
@ -131,6 +139,15 @@
"translation"
]
},
{
"login": "avtkal",
"name": "avtkal",
"avatar_url": "https://avatars.githubusercontent.com/u/63205014?v=4",
"profile": "https://github.com/avtkal",
"contributions": [
"translation"
]
},
{
"login": "cizordj",
"name": "Cézar Augusto",
@ -140,6 +157,15 @@
"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",
"name": "farzadx",
@ -194,6 +220,15 @@
"translation"
]
},
{
"login": "GenosseFlosse",
"name": "GenosseFlosse",
"avatar_url": "https://avatars.githubusercontent.com/u/59205524?v=4",
"profile": "https://github.com/GenosseFlosse",
"contributions": [
"translation"
]
},
{
"login": "kernoeb",
"name": "kernoeb",
@ -275,6 +310,15 @@
"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",
"name": "Still Hsu",
@ -302,6 +346,15 @@
"translation"
]
},
{
"login": "wokija",
"name": "wokija",
"avatar_url": "https://avatars.githubusercontent.com/u/14982166?v=4",
"profile": "https://github.com/wokija",
"contributions": [
"translation"
]
},
{
"login": "ysakamoto",
"name": "ysakamoto",
@ -310,6 +363,15 @@
"contributions": [
"translation"
]
},
{
"login": "ZDVokoun",
"name": "ZDVokoun",
"avatar_url": "https://avatars.githubusercontent.com/u/76393152?v=4",
"profile": "https://github.com/ZDVokoun",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 6,

View File

@ -1,19 +1,16 @@
---
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]"
labels: question
assignees: ''
---
<!--
STOP!!!
STOP!!! STOP!!! STOP!!!
General questions & feedback should be submitted to
* one of our chatrooms (see README.md), or
* the GitHub discussions section, or
* r/barinsta on reddit.
STOP!!!
-->
STOP!!! STOP!!! STOP!!!

View File

@ -36,5 +36,10 @@
<option name="name" value="maven" />
<option name="url" value="http://maven.geotoolkit.org/" />
</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>
</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%"/>
@ -9,10 +9,10 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-32-orange.svg)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-39-orange.svg)](#contributors)
<!-- 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).
@ -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)
## 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/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>
@ -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.
* 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)
* 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
@ -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://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/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="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>
<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="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://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/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/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/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://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>
<td align="center"><a href="https://janek.xyz/"><img src="https://avatars3.githubusercontent.com/u/8365659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Janek</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/rohang02"><img src="https://avatars3.githubusercontent.com/u/47921164?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rohang02</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/retiolus"><img src="https://avatars1.githubusercontent.com/u/65604466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>retiolus</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/rikishi0071"><img src="https://avatars3.githubusercontent.com/u/18183855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rikishi0071</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://stillu.cc/"><img src="https://avatars2.githubusercontent.com/u/5843208?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Still Hsu</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/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>
</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/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>
</table>
@ -103,7 +114,7 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
## 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
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
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)
[![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
targetSdkVersion 29
versionCode 57
versionName '19.0.5'
versionCode 60
versionName '19.1.0'
multiDexEnabled true
@ -56,13 +56,13 @@ configurations.all {
}
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 nav_version = '2.3.2'
def exoplayer_version = '2.12.0'
def nav_version = '2.3.4'
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-dash:$exoplayer_version"
@ -70,24 +70,36 @@ dependencies {
implementation "androidx.appcompat:appcompat:$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.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
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.lifecycle:lifecycle-extensions:2.2.0"
implementation 'com.google.guava:guava:27.0.1-android'
implementation 'com.google.guava:guava:27.1-jre'
// Room
def room_version = "2.2.5"
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-guava:$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 'com.facebook.fresco:fresco:2.3.0'
@ -99,8 +111,11 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
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
#-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.WRITE_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
android:name=".InstaGrabberApplication"
@ -15,13 +22,13 @@
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme.Light.White"
android:theme="@style/AppTheme.Launcher"
tools:ignore="UnusedAttribute">
<activity
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:taskAffinity=".Main"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
@ -30,8 +37,8 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<!--<action android:name="android.intent.action.SEARCH" />-->
<!--<action android:name="android.intent.action.WEB_SEARCH" />-->
<!-- <action android:name="android.intent.action.SEARCH" /> -->
<!-- <action android:name="android.intent.action.WEB_SEARCH" /> -->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@ -114,6 +121,15 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</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
android:name="androidx.core.content.FileProvider"
@ -131,5 +147,27 @@
<service
android:name=".services.DeleteImageIntentService"
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>
</manifest>

View File

@ -3,6 +3,7 @@ package awais.instagrabber;
import android.app.Application;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco;
@ -15,13 +16,16 @@ import java.util.UUID;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.CrashReporter;
import awaisomereport.LogCollector;
//import awaisomereport.LogCollector;
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.datetimeParser;
import static awais.instagrabber.utils.Utils.logCollector;
//import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class InstaGrabberApplication extends Application {
@ -52,13 +56,21 @@ public final class InstaGrabberApplication extends Application {
}
if (!BuildConfig.DEBUG) CrashReporter.get(this).start();
logCollector = new LogCollector(this);
// logCollector = new LogCollector(this);
CookieHandler.setDefault(NET_COOKIE_MANAGER);
if (settingsHelper == null)
settingsHelper = new SettingsHelper(this);
if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper());
}
if (cacheDir == null) {
cacheDir = getCacheDir().getAbsolutePath();
}
LocaleUtils.setLocale(getBaseContext());
if (clipboardManager == null)
@ -70,6 +82,8 @@ public final class InstaGrabberApplication extends Application {
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) :
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.asyncs.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.enums.IntentModelType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
@ -123,7 +123,7 @@ public final class DirectDownload extends AppCompatActivity {
return;
}
final String text = model.getText();
new PostFetcher(text, new FetchListener<FeedModel>() {
new PostFetcher(text, new FetchListener<Media>() {
@Override
public void doBefore() {
if (notificationManager == null) return;
@ -138,7 +138,7 @@ public final class DirectDownload extends AppCompatActivity {
}
@Override
public void onResult(final FeedModel result) {
public void onResult(final Media result) {
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (result == null) {
finish();

View File

@ -1,5 +1,6 @@
package awais.instagrabber.activities;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -20,19 +21,26 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
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.FragmentManager;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController;
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.CollapsingToolbarLayout;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.ArrayList;
@ -53,19 +62,27 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.services.DMSyncAlarmReceiver;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.IntentUtils;
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.Utils.settingsHelper;
@ -93,6 +110,8 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private int firstFragmentGraphIndex;
private boolean isActivityCheckerServiceBound = false;
private boolean isBackStackEmpty = false;
private boolean isLoggedIn;
private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
@ -122,10 +141,20 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
binding = ActivityMainBinding.inflate(getLayoutInflater());
final String cookie = settingsHelper.getString(Constants.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());
final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar);
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) {
setupBottomNavigationBar(true);
}
@ -133,12 +162,27 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES);
if (checkUpdates) FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
new ViewModelProvider(this).get(AppStateViewModel.class); // Just initiate the App state here
final Intent intent = getIntent();
handleIntent(intent);
if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) {
bindActivityCheckerService();
}
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
@ -206,7 +250,17 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override
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();
return;
}
@ -227,15 +281,22 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
}
private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
Constants.DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
Constants.ACTIVITY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
Constants.DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
Constants.ACTIVITY_CHANNEL_NAME,
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() {
@ -372,8 +433,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private void setupBottomNavigationBar(final boolean setDefaultFromSettings) {
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) {
main_nav_ids = R.array.logged_out_main_nav_ids;
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
@ -449,12 +508,28 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (navController == null) return;
NavigationUI.setupWithNavController(toolbar, navController);
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
binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId();
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
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();
return;
}
if (Constants.ACTION_SHOW_DM_THREAD.equals(action)) {
showThread(intent);
return;
}
if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.equals("text/plain")) {
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) {
if (url == null) return;
// Log.d(TAG, url);
@ -583,7 +714,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("hashtag", "#" + hashtag);
bundle.putString("hashtag", hashtag);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
}
@ -591,7 +722,9 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (currentNavControllerLiveData == null) return;
final NavController navController = currentNavControllerLiveData.getValue();
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() {
@ -637,4 +770,44 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
public CollapsingToolbarLayout getCollapsingToolbarView() {
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 androidx.annotation.NonNull;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import java.util.List;
import java.util.Objects;
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 static final DiffUtil.ItemCallback<InboxThreadModel> diffCallback = new DiffUtil.ItemCallback<InboxThreadModel>() {
private static final DiffUtil.ItemCallback<DirectThread> diffCallback = new DiffUtil.ItemCallback<DirectThread>() {
@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());
}
@Override
public boolean areContentsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) {
return oldItem.equals(newItem);
public boolean areContentsTheSame(@NonNull final DirectThread oldThread,
@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) {
super(diffCallback);
super(new AsyncDifferConfig.Builder<>(diffCallback).build());
this.onClickListener = onClickListener;
}
@NonNull
@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 LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);
return new DirectMessageInboxItemViewHolder(binding);
return new DirectInboxItemViewHolder(binding, onClickListener);
}
@Override
public void onBindViewHolder(@NonNull final DirectMessageInboxItemViewHolder holder, final int position) {
final InboxThreadModel threadModel = getItem(position);
if (onClickListener != null) {
holder.itemView.setOnClickListener((v) -> onClickListener.onItemClick(threadModel));
}
holder.bind(threadModel);
public void onBindViewHolder(@NonNull final DirectInboxItemViewHolder holder, final int position) {
final DirectThread thread = getItem(position);
holder.bind(thread);
}
@Override
public long getItemId(final int position) {
return getItem(position).getThreadId().hashCode();
}
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.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> {
private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {
@ -21,7 +22,8 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
@Override
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());
}
};
@ -38,7 +40,7 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
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, onTopicClickListener);
return new TopicClusterViewHolder(binding, onTopicClickListener, null);
}
@Override

View File

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

View File

@ -2,20 +2,21 @@ package awais.instagrabber.adapters;
import android.view.View;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
@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
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {}
public void onProfilePicClick(final Media media, final View profilePicView) {}
@Override
public void onNameClick(final FeedModel feedModel, final View profilePicView) {}
public void onNameClick(final Media media, final View profilePicView) {}
@Override
public void onLocationClick(final FeedModel feedModel) {}
public void onLocationClick(final Media media) {}
@Override
public void onMentionClick(final String mention) {}
@ -24,10 +25,10 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onHashtagClick(final String hashtag) {}
@Override
public void onCommentsClick(final FeedModel feedModel) {}
public void onCommentsClick(final Media media) {}
@Override
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {}
public void onDownloadClick(final Media media, final int childPosition) {}
@Override
public void onEmailClick(final String emailId) {}
@ -36,5 +37,5 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onURLClick(final String url) {}
@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.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding;
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 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>() {
@Override
@ -37,6 +60,17 @@ public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, St
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
@Override
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;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -12,13 +11,13 @@ import java.util.List;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final List<ProfileModel> profileModels;
private final List<User> profileModels;
private final View.OnClickListener onClickListener;
public LikesAdapter(final List<ProfileModel> profileModels,
public LikesAdapter(final List<User> profileModels,
final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.onClickListener = onClickListener;
@ -34,7 +33,7 @@ public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder>
@Override
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);
}

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.Collections;
import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.NotificationModel;
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 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
public boolean areItemsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
return oldItem.getId().equals(newItem.getId());
public boolean areItemsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
@Override
public boolean areContentsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
return oldItem.getId().equals(newItem.getId());
public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
};
public NotificationsAdapter(final OnNotificationClickListener notificationClickListener,
final MentionClickListener mentionClickListener) {
public NotificationsAdapter(final OnNotificationClickListener notificationClickListener) {
super(DIFF_CALLBACK);
this.notificationClickListener = notificationClickListener;
this.mentionClickListener = mentionClickListener;
}
@NonNull
@ -51,12 +48,12 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
@Override
public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {
final NotificationModel notificationModel = getItem(position);
holder.bind(notificationModel, notificationClickListener);
final Notification Notification = getItem(position);
holder.bind(Notification, notificationClickListener);
}
@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) {
super.submitList(null, commitCallback);
return;
@ -65,7 +62,7 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
}
@Override
public void submitList(@Nullable final List<NotificationModel> list) {
public void submitList(@Nullable final List<Notification> list) {
if (list == null) {
super.submitList(null);
return;
@ -73,8 +70,10 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
super.submitList(sort(list));
}
private List<NotificationModel> sort(final List<NotificationModel> list) {
final List<NotificationModel> listCopy = new ArrayList<>(list);
private List<Notification> sort(final List<Notification> list) {
final List<Notification> listCopy = new ArrayList<>(list).stream()
.filter(i -> i.getType() != null)
.collect(Collectors.toList());
Collections.sort(listCopy, (o1, o2) -> {
// keep requests at top
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 (o2.getType() == NotificationType.REQUEST) return 1;
// timestamp
return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
return Double.compare(o2.getArgs().getTimestamp(), o1.getArgs().getTimestamp());
});
return listCopy;
}
public interface OnNotificationClickListener {
void onNotificationClick(final NotificationModel model);
void onNotificationClick(final Notification model);
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
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.LayoutExoCustomControlsBinding;
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
import awais.instagrabber.models.PostChild;
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 boolean loadVideoOnItemClick;
private final SliderCallback sliderCallback;
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
public boolean areItemsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
@Override
public boolean areContentsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
};
@ -66,14 +66,14 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
@Override
public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {
final PostChild model = getItem(position);
holder.bind(model, position, sliderCallback);
final Media media = getItem(position);
holder.bind(media, position, sliderCallback);
}
@Override
public int getItemViewType(final int position) {
final PostChild viewerPostModel = getItem(position);
return viewerPostModel.getItemType().getId();
final Media media = getItem(position);
return media.getMediaType().getId();
}
// @NonNull
@ -148,5 +148,7 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
void onPlayerPlay(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.PipelineDraweeControllerBuilder;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
@ -20,9 +21,10 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.DownloadedCheckerAsyncTask;
import awais.instagrabber.databinding.ItemFeedGridBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
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 static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;
@ -36,7 +38,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
}
public void bind(final int position,
@NonNull final FeedModel feedModel,
@NonNull final Media media,
@NonNull final PostsLayoutPreferences layoutPreferences,
final FeedAdapterV2.FeedItemCallback feedItemCallback,
final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
@ -44,28 +46,121 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
final boolean selected) {
itemView.setOnClickListener(v -> {
if (!selectionModeActive && feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage);
feedItemCallback.onPostClick(media, binding.profilePic, binding.postImage);
return;
}
if (selectionModeActive && adapterSelectionCallback != null) {
adapterSelectionCallback.onPostClick(position, feedModel);
adapterSelectionCallback.onPostClick(position, media);
}
});
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);
// for rounded borders (clip view to background shape)
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
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);
} else {
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()) {
binding.profilePic.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getSdProfilePic()) ? View.GONE : View.VISIBLE);
binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic());
if (user == null) {
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();
@DimenRes final int dimenRes;
switch (layoutPreferences.getProfilePicSize()) {
@ -88,74 +183,19 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.profilePic.setVisibility(View.GONE);
}
if (layoutPreferences.isNameVisible()) {
binding.name.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getUsername()) ? View.GONE : View.VISIBLE);
binding.name.setText(feedModel.getProfileModel().getUsername());
if (user == null) {
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 {
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.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
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);
return true;
});
final ProfileModel profileModel = model.getProfileModel();
final User profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername());
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);
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;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
@ -9,7 +8,7 @@ import java.util.List;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
@ -20,18 +19,18 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
this.binding = binding;
}
public void bind(final ProfileModel model,
public void bind(final User model,
final List<Long> admins,
final View.OnClickListener onClickListener) {
if (model == null) return;
itemView.setTag(model);
itemView.setOnClickListener(onClickListener);
binding.tvUsername.setText(model.getUsername());
binding.tvFullName.setText(model.getName());
if (admins != null && admins.contains(Long.parseLong(model.getId()))) {
binding.tvFullName.setText(model.getFullName());
if (admins != null && admins.contains(model.getPk())) {
binding.isAdmin.setVisibility(View.VISIBLE);
}
binding.ivProfilePic.setImageURI(model.getSdProfilePic());
binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
}
public void bind(final FollowModel model,

View File

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

View File

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

View File

@ -1,19 +1,25 @@
package awais.instagrabber.adapters.viewholder;
import android.annotation.SuppressLint;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import java.util.List;
import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.customviews.VerticalDragHelper;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
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.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -24,6 +30,14 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
private final LayoutVideoPlayerWithThumbnailBinding binding;
private final LayoutExoCustomControlsBinding controlsBinding;
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;
@SuppressLint("ClickableViewAccessibility")
@ -35,29 +49,27 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
this.binding = binding;
this.controlsBinding = controlsBinding;
this.loadVideoOnItemClick = loadVideoOnItemClick;
if (onVerticalDragListener != null) {
final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
binding.thumbnailParent.setOnTouchListener((v, event) -> {
final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
});
binding.playerView.setOnTouchListener((v, event) -> {
final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event);
if (onDragTouch) {
return true;
}
return playerVerticalDragHelper.onGestureTouchEvent(event);
});
}
// if (onVerticalDragListener != null) {
// final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
// final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
// thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
// playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
// binding.thumbnailParent.setOnTouchListener((v, event) -> {
// final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
// if (onDragTouch) {
// return true;
// }
// return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
// });
// }
final GestureDetector gestureDetector = new GestureDetector(itemView.getContext(), videoPlayerViewGestureListener);
binding.playerView.setOnTouchListener((v, event) -> {
gestureDetector.onTouchEvent(event);
return true;
});
}
public void bind(@NonNull final PostChild model,
public void bind(@NonNull final Media media,
final int position,
final SliderItemsAdapter.SliderCallback sliderCallback) {
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);
final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();
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.height = resultingHeight;
binding.playerView.requestLayout();
@ -102,14 +114,30 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
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(),
binding,
model.getDisplayUrl(),
videoUrl,
vol,
aspectRatio,
model.getThumbnailUrl(),
ResponseBodyUtils.getThumbUrl(media),
loadVideoOnItemClick,
controlsBinding,
videoPlayerCallback);

View File

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

View File

@ -25,18 +25,24 @@ import java.util.concurrent.atomic.AtomicInteger;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
import awais.instagrabber.adapters.SavedCollectionsAdapter;
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 {
private final ItemDiscoverTopicBinding binding;
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
private final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener;
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener,
final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onTopicClickListener = onTopicClickListener;
this.onCollectionClickListener = onCollectionClickListener;
}
public void bind(final TopicCluster topicCluster) {
@ -57,43 +63,115 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
}
// binding.title.setTransitionName("title-" + topicCluster.getId());
binding.cover.setTransitionName("cover-" + topicCluster.getId());
final ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl()))
.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()) {
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedia());
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 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();
}
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);
});
}
}
}, CallerThreadExecutor.getInstance());
binding.cover.setImageRequest(imageRequest);
}
binding.title.setText(topicCluster.getTitle());
}
@Override
public void onFailureImpl(@NonNull DataSource dataSource) {
dataSource.close();
}
}, CallerThreadExecutor.getInstance());
binding.cover.setImageRequest(imageRequest);
public void bind(final SavedCollection topicCluster) {
if (topicCluster == null) {
return;
}
final AtomicInteger titleColor = new AtomicInteger(-1);
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());
}
}

View File

@ -9,7 +9,7 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
final User profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
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.databinding.ItemCommentBinding;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
final User profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
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.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
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 static android.text.TextUtils.TruncateAt.END;
@ -35,52 +37,60 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
this.feedItemCallback = feedItemCallback;
}
public void bind(final FeedModel feedModel) {
if (feedModel == null) {
public void bind(final Media media) {
if (media == null) {
return;
}
setupProfilePic(feedModel);
setupLocation(feedModel);
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
setupComments(feedModel);
setupCaption(feedModel);
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER) {
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, -1));
setupProfilePic(media);
setupLocation(media);
bottomBinding.tvPostDate.setText(media.getDate());
setupComments(media);
setupCaption(media);
if (media.getMediaType() != MediaItemType.MEDIA_TYPE_SLIDER) {
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(media, -1));
}
bindItem(feedModel);
bindItem(media);
}
private void setupComments(final FeedModel feedModel) {
final long commentsCount = feedModel.getCommentsCount();
private void setupComments(@NonNull final Media feedModel) {
final long commentsCount = feedModel.getCommentCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
}
private void setupProfilePic(final FeedModel feedModel) {
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic));
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
setupTitle(feedModel);
private void setupProfilePic(@NonNull final Media media) {
final User user = media.getUser();
if (user == null) {
topBinding.ivProfilePic.setVisibility(View.GONE);
topBinding.title.setVisibility(View.GONE);
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 SpannableString spannableString = new SpannableString();
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
final ProfileModel profileModel = feedModel.getProfileModel();
final String title = "@" + profileModel.getUsername();
final User user = media.getUser();
final String title = "@" + user.getUsername();
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.clearOnHashtagClickListeners();
bottomBinding.viewerCaption.clearOnURLClickListeners();
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);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (captionEmpty) return;
@ -103,22 +113,31 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
}
private void setupLocation(final FeedModel feedModel) {
final String locationName = feedModel.getLocationName();
if (TextUtils.isEmpty(locationName)) {
private void setupLocation(@NonNull final Media media) {
final Location location = media.getLocation();
if (location == null) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
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(feedModel));
}
else {
final String locationName = location.getName();
if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
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.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class FeedPhotoViewHolder extends FeedItemViewHolder {
@ -30,7 +31,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
@ -40,14 +41,12 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
public void bindItem(final Media media) {
if (media == null) return;
binding.getRoot().post(() -> {
setDimensions(feedModel);
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();
setDimensions(media);
final String thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
String url = ResponseBodyUtils.getImageUrl(media);
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
// .setLocalThumbnailPreviewsEnabled(true)
@ -62,7 +61,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true;
}
return false;
@ -71,8 +70,8 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
});
}
private void setDimensions(final FeedModel feedModel) {
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
private void setDimensions(final Media feedModel) {
final float aspectRatio = (float) feedModel.getOriginalWidth() / feedModel.getOriginalHeight();
binding.imageViewer.setAspectRatio(aspectRatio);
}
}

View File

@ -13,8 +13,7 @@ import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.SliderCallbackAdapter;
import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
@ -30,7 +29,7 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
binding.itemFeedBottom.btnViews.setVisibility(View.GONE);
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
@ -39,8 +38,8 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
final List<PostChild> sliderItems = feedModel.getSliderItems();
public void bindItem(final Media feedModel) {
final List<Media> sliderItems = feedModel.getCarouselMedia();
final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;
if (sliderItemLen <= 0) return;
final String text = "1/" + sliderItemLen;
@ -68,7 +67,7 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
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();
int requiredWidth = layoutParams.width;
if (requiredWidth <= 0) {
@ -86,10 +85,10 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
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 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;
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.SimpleCache;
import java.util.List;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper;
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.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -32,7 +36,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private final DefaultDataSourceFactory dataSourceFactory;
private CacheDataSourceFactory cacheDataSourceFactory;
private FeedModel feedModel;
private Media media;
// private final Runnable loadRunnable = new Runnable() {
// @Override
@ -46,7 +50,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
this.feedItemCallback = feedItemCallback;
binding.itemFeedBottom.tvVideoViews.setVisibility(View.VISIBLE);
binding.itemFeedBottom.btnViews.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper());
final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
@ -57,68 +61,55 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
public void bindItem(final Media media) {
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
this.feedModel = feedModel;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
// showOrHideDetails(false);
this.media = media;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(media.getViewCount()));
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
@Override
public void onThumbnailClick() {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
}
@Override
public void onPlayerViewLoaded() {
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
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.height = resultingHeight;
binding.videoPost.playerView.requestLayout();
setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
}
};
// final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
// final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
// final Uri uri = Uri.parse(feedModel.getDisplayUrl());
// final MediaItem mediaItem = MediaItem.fromUri(uri);
// final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
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);
videoUrl = videoVersion.getUrl();
}
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
binding.videoPost,
feedModel.getDisplayUrl(),
videoUrl,
vol,
aspectRatio,
feedModel.getThumbnailUrl(),
ResponseBodyUtils.getThumbUrl(media),
false,
null,
videoPlayerCallback);
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();
layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
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());
}
private void setMuteIcon(final float vol) {
// binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
}
public FeedModel getCurrentFeedModel() {
return feedModel;
public Media getCurrentFeedModel() {
return media;
}
// 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.interfaces.FetchListener;
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.NetworkUtils;
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>> {
private static final String TAG = "CommentsFetcher";
@ -106,44 +107,34 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
if (childComment != null) {
final JSONObject owner = childComment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false,
false,
false,
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
final User user = new User(
owner.optLong(Constants.EXTRAS_ID, 0),
owner.getString(Constants.EXTRAS_USERNAME),
null,
false,
owner.getString("profile_pic_url"),
null,
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null,
null, null, null);
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
childComment.getString("text"),
childComment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
profileModel));
user));
}
}
}
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e,
LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
"getChildComments",
new Pair<>("commentModels.size", commentModels.size()));
// if (logCollector != null)
// logCollector.appendException(e,
// LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
// "getChildComments",
// new Pair<>("commentModels.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
if (fetchListener != null) fetchListener.onFailure(e);
break;
@ -156,138 +147,122 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull
private synchronized List<CommentModel> getParentComments() {
final List<CommentModel> commentModels = new ArrayList<>();
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
conn.connect();
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media")
.getJSONObject(
"edge_media_to_parent_comment");
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media")
.getJSONObject(
"edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
final String foundEndCursor = pageInfo.optString("end_cursor");
final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor));
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
final String foundEndCursor = pageInfo.optString("end_cursor");
final boolean hasNextPage = pageInfo.optBoolean("has_next_page", !TextUtils.isEmpty(foundEndCursor));
// final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
// final JSONObject endCursorObject = new JSONObject(endCursor);
// endCursor = endCursorObject.optString("cached_comments_cursor");
//
// if (!Utils.isEmpty(endCursor))
// endCursor = "{\\\"cached_comments_cursor\\\": \\\"" + endCursor + "\\\", ";
// else
// endCursor = "{";
//
// endCursor = endCursor + "\\\"bifilter_token\\\": \\\"" + endCursorObject.getString("bifilter_token") + "\\\"}";
// }
// else if (containsToken) endCursor = null;
// final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
// final JSONObject endCursorObject = new JSONObject(endCursor);
// endCursor = endCursorObject.optString("cached_comments_cursor");
//
// if (!Utils.isEmpty(endCursor))
// endCursor = "{\\\"cached_comments_cursor\\\": \\\"" + endCursor + "\\\", ";
// else
// endCursor = "{";
//
// endCursor = endCursor + "\\\"bifilter_token\\\": \\\"" + endCursorObject.getString("bifilter_token") + "\\\"}";
// }
// else if (containsToken) endCursor = null;
final JSONArray comments = parentComments.getJSONArray("edges");
final int commentsLen = comments.length();
for (int i = 0; i < commentsLen; ++i) {
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
final JSONArray comments = parentComments.getJSONArray("edges");
final int commentsLen = comments.length();
for (int i = 0; i < commentsLen; ++i) {
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
final JSONObject owner = comment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false,
false,
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
final JSONObject owner = comment.getJSONObject("owner");
final User user = new User(
owner.optLong(Constants.EXTRAS_ID, 0),
owner.getString(Constants.EXTRAS_USERNAME),
null,
false,
owner.getString("profile_pic_url"),
null,
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
owner.optBoolean("is_verified"),
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null, null,
null, null);
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
final String commentId = comment.getString(Constants.EXTRAS_ID);
final CommentModel commentModel = new CommentModel(commentId,
comment.getString("text"),
comment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
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 commentId = comment.getString(Constants.EXTRAS_ID);
final CommentModel commentModel = new CommentModel(commentId,
comment.getString("text"),
comment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"),
profileModel);
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject;
final JSONArray childCommentsArray;
final int childCommentsLen;
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);
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;
}
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;
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 User childUser = new User(
tempJsonObject.optLong(Constants.EXTRAS_ID, 0),
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;
}
}

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;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import awais.instagrabber.customviews.helpers.PostFetcher;
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.ServiceCallback;
@ -20,18 +25,28 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
}
@Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
public void fetch(final FetchListener<List<Media>> fetchListener) {
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<TopicalExploreFeedResponse>() {
@Override
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
public void onSuccess(final TopicalExploreFeedResponse result) {
if (result == null) {
onFailure(new RuntimeException("result is null"));
return;
}
moreAvailable = result.isMoreAvailable();
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) {
fetchListener.onResult(result.getItems());
fetchListener.onResult(posts);
}
}
@ -46,7 +61,7 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
@Override
public void reset() {
topicalExploreRequest.setMaxId(-1);
topicalExploreRequest.setMaxId(null);
}
@Override

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