1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-26 08:37:29 +00:00

Merge branch 'master' into retrofit-intercept-errors

This commit is contained in:
Austin Huang 2021-03-27 16:06:53 -04:00 committed by GitHub
commit 6965fbdc9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 2096 additions and 1427 deletions

View File

@ -42,15 +42,6 @@
"bug" "bug"
] ]
}, },
{
"login": "Zopieux",
"name": "Alexandre Macabies",
"avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
"profile": "https://github.com/Zopieux",
"contributions": [
"code"
]
},
{ {
"login": "MeLlamoPablo", "login": "MeLlamoPablo",
"name": "Pablo Rodríguez", "name": "Pablo Rodríguez",
@ -61,10 +52,10 @@
] ]
}, },
{ {
"login": "AwaisKing", "login": "Zopieux",
"name": "AWAiS", "name": "Alexandre Macabies",
"avatar_url": "https://avatars3.githubusercontent.com/u/5278488", "avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
"profile": "http://rerolledgeek.blogspot.com/", "profile": "https://github.com/Zopieux",
"contributions": [ "contributions": [
"code" "code"
] ]
@ -119,16 +110,6 @@
"translation" "translation"
] ]
}, },
{
"login": "e-edgren",
"name": "Airikr",
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
"profile": "https://airikr.me/",
"contributions": [
"ideas",
"question"
]
},
{ {
"login": "Akrai", "login": "Akrai",
"name": "Akrai", "name": "Akrai",
@ -274,6 +255,15 @@
"translation" "translation"
] ]
}, },
{
"login": "Pyrobauve",
"name": "Pyrobauve",
"avatar_url": "https://avatars.githubusercontent.com/u/48654473?v=4",
"profile": "https://github.com/Pyrobauve",
"contributions": [
"translation"
]
},
{ {
"login": "RAMAR-RAR", "login": "RAMAR-RAR",
"name": "RAMAR-RAR", "name": "RAMAR-RAR",
@ -319,6 +309,15 @@
"translation" "translation"
] ]
}, },
{
"login": "Sitavi",
"name": "Sitavi",
"avatar_url": "https://avatars.githubusercontent.com/u/80586127?v=4",
"profile": "https://github.com/Sitavi",
"contributions": [
"translation"
]
},
{ {
"login": "Still34", "login": "Still34",
"name": "Still Hsu", "name": "Still Hsu",

View File

@ -0,0 +1,67 @@
name: Github nightly
on:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '27 10 * * *' # Everyday at 10:27:00
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build Github unsigned apk
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre
- name: Sign APK
uses: r0adkll/sign-android-release@v1
# ID used to access action output
id: sign_app
with:
releaseDirectory: app/build/outputs/apk/github/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Get current date and time
id: date
run: echo "::set-output name=date::$(date +'%Y%m%d_%H%M%S')"
# Create artifact
- name: Create apk artifact
uses: actions/upload-artifact@v2
with:
name: barinsta_nightly_${{ steps.date.outputs.date }}
path: ${{steps.sign_app.outputs.signedReleaseFile}}
# Send success notification
- name: Send success Telegram notification
if: ${{ success() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
document: ${{steps.sign_app.outputs.signedReleaseFile}}
# Send failure notification
- name: Send failure Telegram notification
if: ${{ failure() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"

View File

@ -0,0 +1,68 @@
name: Github pre-release
on: workflow_dispatch
# push:
# branches: [ master ]
# pull_request:
# branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build Github unsigned pre-release apk
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre
- name: Sign APK
uses: r0adkll/sign-android-release@v1
# ID used to access action output
id: sign_app
with:
releaseDirectory: app/build/outputs/apk/github/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Get current date and time
id: date
run: echo "::set-output name=date::$(date +'%Y%m%d_%H%M%S')"
# Create artifact
- name: Create apk artifact
uses: actions/upload-artifact@v2
with:
name: barinsta_pre-release_${{ steps.date.outputs.date }}
path: ${{steps.sign_app.outputs.signedReleaseFile}}
# Send success notification
- name: Send success Telegram notification
if: ${{ success() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
document: ${{steps.sign_app.outputs.signedReleaseFile}}
# Send failure notification
- name: Send failure Telegram notification
if: ${{ failure() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"

18
.github/workflows/label-bugs.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Label bugs
on:
issues:
types: [opened]
jobs:
add-labels:
runs-on: ubuntu-latest
if: contains(github.event.issue.body, 'New Trace collected:') == true
steps:
- name: Add labels
uses: actions-cool/issues-helper@v2.2.1
with:
actions: 'add-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'bug'

18
.github/workflows/label-duplicates.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Label duplicates
on:
issue_comment:
types: [created]
jobs:
add-labels:
runs-on: ubuntu-latest
if: contains(github.event.comment.body, 'Duplicate of') == true
steps:
- name: Add labels
uses: actions-cool/issues-helper@v2.2.1
with:
actions: 'add-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'duplicate'

3
.gitignore vendored
View File

@ -17,3 +17,6 @@
.externalNativeBuild .externalNativeBuild
.cxx .cxx
app/release app/release
/sentry.properties
/app/fdroid/
/app/github/

View File

@ -1 +1 @@
InstaGrabber Barinsta

View File

@ -57,46 +57,46 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
<td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td> <td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td> <td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/Zopieux"><img src="https://avatars.githubusercontent.com/u/81353?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandre Macabies</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=Zopieux" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MeLlamoPablo"><img src="https://avatars.githubusercontent.com/u/11708035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pablo Rodríguez</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=MeLlamoPablo" title="Code">💻</a></td> <td align="center"><a href="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://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://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>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://snajdovski.github.io"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/CrazyMarvin"><img src="https://avatars3.githubusercontent.com/u/15004217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CrazyMarvin</b></sub></a><br /><a href="#financial-CrazyMarvin" title="Financial">💵</a></td> <td align="center"><a href="https://github.com/CrazyMarvin"><img src="https://avatars3.githubusercontent.com/u/15004217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CrazyMarvin</b></sub></a><br /><a href="#financial-CrazyMarvin" title="Financial">💵</a></td>
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td> <td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td> <td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/RickyM7"><img src="https://avatars3.githubusercontent.com/u/24703825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3ARickyM7" title="Bug reports">🐛</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/RickyM7"><img src="https://avatars3.githubusercontent.com/u/24703825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3ARickyM7" title="Bug reports">🐛</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td>
</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/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/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>
</tr>
<tr>
<td align="center"><a href="https://github.com/cizordj"><img src="https://avatars2.githubusercontent.com/u/32869222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cézar Augusto</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/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/dimitrist19"><img src="https://avatars.githubusercontent.com/u/56406468?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dimitris T</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/initdebugs"><img src="https://avatars0.githubusercontent.com/u/75781464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Initdebugs</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/initdebugs"><img src="https://avatars0.githubusercontent.com/u/75781464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Initdebugs</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://janek.xyz/"><img src="https://avatars3.githubusercontent.com/u/8365659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Janek</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://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://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> <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://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/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>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Pyrobauve"><img src="https://avatars.githubusercontent.com/u/48654473?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pyrobauve</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/rohang02"><img src="https://avatars3.githubusercontent.com/u/47921164?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rohang02</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/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/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>
</tr>
<tr>
<td align="center"><a href="https://github.com/rikishi0071"><img src="https://avatars3.githubusercontent.com/u/18183855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rikishi0071</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://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://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://github.com/Sitavi"><img src="https://avatars.githubusercontent.com/u/80586127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sitavi</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://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/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>

View File

@ -1,5 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs" apply plugin: "androidx.navigation.safeargs"
apply from: 'sentry.gradle'
def getGitHash = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
android { android {
compileSdkVersion 29 compileSdkVersion 29
@ -48,8 +58,41 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
flavorDimensions "repo"
productFlavors {
github {
dimension "repo"
// versionNameSuffix "-github" // appended in assemble task
buildConfigField("String", "dsn", SENTRY_DSN)
} }
fdroid {
dimension "repo"
versionNameSuffix "-fdroid"
}
}
android.applicationVariants.all { variant ->
if (variant.flavorName != "github") return
variant.outputs.all { output ->
def builtType = variant.buildType.name
def versionName = variant.versionName
// def versionCode = variant.versionCode
def flavor = variant.flavorName
def suffix = "${versionName}-${flavor}_${builtType}" // eg. 19.1.0-github_debug or release
if (builtType.toString() == 'release' && project.hasProperty("pre")) {
// append latest commit short hash for pre-release
suffix = "${versionName}.${getGitHash()}-${flavor}" // eg. 19.1.0.b123456-github
}
output.versionNameOverride = suffix
outputFileName = "barinsta_${suffix}.apk"
}
}
}
configurations.all { configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds' resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
@ -70,7 +113,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version" implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation "androidx.recyclerview:recyclerview:1.2.0-beta02" implementation "androidx.recyclerview:recyclerview:1.2.0-rc01"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version" implementation "androidx.navigation:navigation-fragment:$nav_version"
@ -81,7 +124,7 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.palette:palette:1.0.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation 'com.google.guava:guava:27.1-jre' implementation 'com.google.guava:guava:27.0.1-android'
// Room // Room
def room_version = "2.2.6" def room_version = "2.2.6"
@ -90,7 +133,7 @@ dependencies {
annotationProcessor "androidx.room:room-compiler:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
// CameraX // CameraX
def camerax_version = "1.1.0-alpha02" def camerax_version = "1.1.0-alpha03"
implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha22" implementation "androidx.camera:camera-view:1.0.0-alpha22"
@ -114,8 +157,9 @@ dependencies {
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2' implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4' implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
githubImplementation 'io.sentry:sentry-android:4.3.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
} }

13
app/sentry.gradle Normal file
View File

@ -0,0 +1,13 @@
def dsnKey = 'DSN'
def defaultDsn = '\"\"'
final Properties properties = new Properties()
File propertiesFile = rootProject.file('sentry.properties')
if (!propertiesFile.exists()) {
propertiesFile.createNewFile()
}
properties.load(new FileInputStream(propertiesFile))
ext{
SENTRY_DSN = properties.getProperty(dsnKey, defaultDsn)
}

View File

@ -0,0 +1,40 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.fragments.settings.IFlavorSettings;
import awais.instagrabber.fragments.settings.SettingCategory;
public final class FlavorSettings implements IFlavorSettings {
private static FlavorSettings instance;
private FlavorSettings() {
}
public static FlavorSettings getInstance() {
if (instance == null) {
instance = new FlavorSettings();
}
return instance;
}
@NonNull
@Override
public List<Preference> getPreferences(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
@NonNull final SettingCategory settingCategory) {
// switch (settingCategory) {
// default:
// break;
// }
return Collections.emptyList();
}
}

View File

@ -0,0 +1,56 @@
package awaisomereport;
import android.app.Application;
import androidx.annotation.NonNull;
public class CrashHandler implements ICrashHandler {
private static final String TAG = CrashHandler.class.getSimpleName();
private final Application application;
public CrashHandler(@NonNull final Application application) {
this.application = application;
}
@Override
public void uncaughtException(@NonNull final Thread t,
@NonNull final Throwable exception,
@NonNull final Thread.UncaughtExceptionHandler defaultEH) {
CrashReporterHelper.startErrorReporterActivity(application, exception);
// zipLogs();
defaultEH.uncaughtException(t, exception);
}
// public synchronized CrashReporter zipLogs() {
// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() :
// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs");
//
// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip);
// final ZipOutputStream zos = new ZipOutputStream(fos)) {
//
// final File[] files = logDir.listFiles();
//
// if (files != null) {
// zos.setLevel(5);
// byte[] buffer;
// for (final File file : files) {
// if (file != null && file.length() > 0) {
// buffer = new byte[1024];
// try (final FileInputStream fis = new FileInputStream(file)) {
// zos.putNextEntry(new ZipEntry(file.getName()));
// int length;
// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length);
// zos.closeEntry();
// }
// }
// }
// }
//
// } catch (final Exception e) {
// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
// }
//
// return this;
// }
}

View File

@ -0,0 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="awais.instagrabber">
<application>
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />
</application>
</manifest>

View File

@ -0,0 +1,83 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.dialogs.ConfirmDialogFragment;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class FlavorSettings implements IFlavorSettings {
private static FlavorSettings instance;
private FlavorSettings() {
}
public static FlavorSettings getInstance() {
if (instance == null) {
instance = new FlavorSettings();
}
return instance;
}
@NonNull
@Override
public List<Preference> getPreferences(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
@NonNull final SettingCategory settingCategory) {
switch (settingCategory) {
case GENERAL:
return getGeneralPrefs(context, fragmentManager);
default:
break;
}
return Collections.emptyList();
}
private List<Preference> getGeneralPrefs(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager) {
return ImmutableList.of(
getSentryPreference(context, fragmentManager)
);
}
private Preference getSentryPreference(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager) {
if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) {
// disabled by default
settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false);
}
return PreferenceHelper.getSwitchPreference(
context,
PREF_ENABLE_SENTRY,
R.string.enable_sentry,
R.string.sentry_summary,
false,
(preference, newValue) -> {
if (!(newValue instanceof Boolean)) return true;
final boolean enabled = (Boolean) newValue;
if (enabled) {
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
111,
0,
R.string.sentry_start_next_launch,
R.string.ok,
0,
0);
dialogFragment.show(fragmentManager, "sentry_dialog");
}
return true;
});
}
}

View File

@ -0,0 +1,59 @@
package awaisomereport;
import android.app.Application;
import androidx.annotation.NonNull;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import io.sentry.SentryLevel;
import io.sentry.android.core.SentryAndroid;
import io.sentry.protocol.Contexts;
import io.sentry.protocol.Device;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class CrashHandler implements ICrashHandler {
private static final String TAG = CrashHandler.class.getSimpleName();
private final Application application;
private final boolean enabled;
public CrashHandler(@NonNull final Application application) {
this.application = application;
if (!settingsHelper.hasPreference(PreferenceKeys.PREF_ENABLE_SENTRY)) {
// disabled by default (change to true if we need enabled by default)
enabled = false;
} else {
enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_SENTRY);
}
if (!enabled) return;
SentryAndroid.init(application, options -> {
options.setDsn(BuildConfig.dsn);
options.setDiagnosticLevel(SentryLevel.ERROR);
options.setBeforeSend((event, hint) -> {
// Removing unneeded info from event
final Contexts contexts = event.getContexts();
final Device device = contexts.getDevice();
device.setName(null);
device.setTimezone(null);
device.setCharging(null);
device.setBootTime(null);
device.setFreeStorage(null);
device.setBatteryTemperature(null);
return event;
});
});
}
@Override
public void uncaughtException(@NonNull final Thread t,
@NonNull final Throwable exception,
@NonNull final Thread.UncaughtExceptionHandler defaultEH) {
// When enabled, Sentry auto captures unhandled exceptions
if (!enabled) {
CrashReporterHelper.startErrorReporterActivity(application, exception);
}
defaultEH.uncaughtException(t, exception);
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="enable_sentry">Enable Sentry</string>
<string name="sentry_summary">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>
<string name="sentry_start_next_launch">Sentry will start on next launch</string>
</resources>

View File

@ -35,16 +35,16 @@ public final class InstaGrabberApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// final Set<RequestListener> requestListeners = new HashSet<>(); CookieHandler.setDefault(NET_COOKIE_MANAGER);
// requestListeners.add(new RequestLoggingListener());
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig if (settingsHelper == null) {
.newBuilder(this) settingsHelper = new SettingsHelper(this);
// .setMainDiskCacheConfig(diskCacheConfig) }
// .setRequestListeners(requestListeners)
.setDownsampleEnabled(true) if (!BuildConfig.DEBUG) {
.build(); CrashReporter.get(this).start();
Fresco.initialize(this, imagePipelineConfig); }
// FLog.setMinimumLoggingLevel(FLog.VERBOSE); // logCollector = new LogCollector(this);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
try { try {
@ -56,13 +56,16 @@ public final class InstaGrabberApplication extends Application {
} }
} }
if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); // final Set<RequestListener> requestListeners = new HashSet<>();
// logCollector = new LogCollector(this); // requestListeners.add(new RequestLoggingListener());
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
CookieHandler.setDefault(NET_COOKIE_MANAGER); .newBuilder(this)
// .setMainDiskCacheConfig(diskCacheConfig)
if (settingsHelper == null) // .setRequestListeners(requestListeners)
settingsHelper = new SettingsHelper(this); .setDownsampleEnabled(true)
.build();
Fresco.initialize(this, imagePipelineConfig);
// FLog.setMinimumLoggingLevel(FLog.VERBOSE);
if (applicationHandler == null) { if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper()); applicationHandler = new Handler(getApplicationContext().getMainLooper());

View File

@ -11,7 +11,6 @@ import android.content.ServiceConnection;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -57,21 +56,21 @@ import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.emoji.EmojiVariantManager; import awais.instagrabber.customviews.emoji.EmojiVariantManager;
import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections; import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType; import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.repositories.responses.search.SearchItem;
import awais.instagrabber.repositories.responses.search.SearchResponse;
import awais.instagrabber.services.ActivityCheckerService; import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.services.DMSyncAlarmReceiver; import awais.instagrabber.services.DMSyncAlarmReceiver;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
@ -84,6 +83,10 @@ import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser; import awais.instagrabber.utils.emoji.EmojiParser;
import awais.instagrabber.viewmodels.AppStateViewModel; import awais.instagrabber.viewmodels.AppStateViewModel;
import awais.instagrabber.webservices.RetrofitFactory; import awais.instagrabber.webservices.RetrofitFactory;
import awais.instagrabber.webservices.SearchService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController; import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -106,6 +109,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private SuggestionsAdapter suggestionAdapter; private SuggestionsAdapter suggestionAdapter;
private AutoCompleteTextView searchAutoComplete; private AutoCompleteTextView searchAutoComplete;
private SearchView searchView; private SearchView searchView;
private SearchService searchService;
private boolean showSearch = true; private boolean showSearch = true;
private Handler suggestionsFetchHandler; private Handler suggestionsFetchHandler;
private int firstFragmentGraphIndex; private int firstFragmentGraphIndex;
@ -173,10 +177,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
getSupportFragmentManager().addOnBackStackChangedListener(this); getSupportFragmentManager().addOnBackStackChangedListener(this);
// Initialise the internal map // Initialise the internal map
AppExecutors.getInstance().tasksThread().execute(() -> { AppExecutors.getInstance().tasksThread().execute(() -> {
EmojiParser.getInstance(); EmojiParser.setup(this);
EmojiVariantManager.getInstance(); EmojiVariantManager.getInstance();
}); });
initEmojiCompat(); initEmojiCompat();
searchService = SearchService.getInstance();
// initDmService(); // initDmService();
} }
@ -313,7 +318,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
switch (type) { switch (type) {
case TYPE_LOCATION: case TYPE_LOCATION:
bundle.putString("locationId", query); bundle.putLong("locationId", Long.valueOf(query));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
break; break;
case TYPE_HASHTAG: case TYPE_HASHTAG:
@ -341,51 +346,84 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser; private boolean searchUser;
private boolean searchHash; private boolean searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync; private Call<SearchResponse> prevSuggestionAsync;
private final String[] COLUMNS = { private final String[] COLUMNS = {
BaseColumns._ID, BaseColumns._ID,
Constants.EXTRAS_USERNAME, Constants.EXTRAS_USERNAME,
Constants.EXTRAS_NAME, Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE, Constants.EXTRAS_TYPE,
"query",
"pfp", "pfp",
"verified" "verified"
}; };
private String currentSearchQuery; private String currentSearchQuery;
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() { private final Callback<SearchResponse> cb = new Callback<SearchResponse>() {
@Override @Override
public void doBefore() { public void onResponse(@NonNull final Call<SearchResponse> call,
suggestionAdapter.changeCursor(null); @NonNull final Response<SearchResponse> response) {
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor; final MatrixCursor cursor;
if (result == null) cursor = null; final SearchResponse body = response.body();
if (body == null) {
cursor = null;
return;
}
final List<SearchItem> result = new ArrayList<SearchItem>();
if (isLoggedIn) {
if (body.getList() != null) result.addAll(searchHash ? body.getList()
.stream()
.filter(i -> i.getUser() == null)
.collect(Collectors.toList()) : body.getList());
}
else { else {
if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers());
if (body.getHashtags() != null) result.addAll(body.getHashtags());
if (body.getPlaces() != null) result.addAll(body.getPlaces());
}
cursor = new MatrixCursor(COLUMNS, 0); cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.size(); i++) {
final SuggestionModel suggestionModel = result[i]; final SearchItem suggestionModel = result.get(i);
if (suggestionModel != null) { if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType(); Object[] objects = null;
final Object[] objects = { if (suggestionModel.getUser() != null)
i, objects = new Object[]{
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getName() : suggestionModel.getUsername(), suggestionModel.getPosition(),
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getUsername() : suggestionModel.getName(), suggestionModel.getUser().getUsername(),
suggestionType, suggestionModel.getUser().getFullName(),
suggestionModel.getProfilePic(), SuggestionType.TYPE_USER,
suggestionModel.isVerified()}; suggestionModel.getUser().getUsername(),
if (!searchHash && !searchUser) cursor.addRow(objects); suggestionModel.getUser().getProfilePicUrl(),
else { suggestionModel.getUser().isVerified()};
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG; else if (suggestionModel.getHashtag() != null)
if (searchHash && isCurrHash || !searchHash && !isCurrHash) objects = new Object[]{
suggestionModel.getPosition(),
suggestionModel.getHashtag().getName(),
suggestionModel.getHashtag().getSubtitle(),
SuggestionType.TYPE_HASHTAG,
suggestionModel.getHashtag().getName(),
"res:/" + R.drawable.ic_hashtag,
false};
else if (suggestionModel.getPlace() != null)
objects = new Object[]{
suggestionModel.getPosition(),
suggestionModel.getPlace().getTitle(),
suggestionModel.getPlace().getSubtitle(),
SuggestionType.TYPE_LOCATION,
suggestionModel.getPlace().getLocation().getPk(),
"res:/" + R.drawable.ic_location,
false};
cursor.addRow(objects); cursor.addRow(objects);
} }
} }
}
}
suggestionAdapter.changeCursor(cursor); suggestionAdapter.changeCursor(cursor);
} }
@Override
public void onFailure(@NonNull final Call<SearchResponse> call,
Throwable t) {
if (!call.isCanceled() && t != null)
Log.e(TAG, "Exception on search:", t);
}
}; };
private final Runnable runnable = () -> { private final Runnable runnable = () -> {
@ -404,17 +442,19 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (searchAutoComplete != null) { if (searchAutoComplete != null) {
searchAutoComplete.setThreshold(1); searchAutoComplete.setThreshold(1);
} }
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor( prevSuggestionAsync = searchService.search(isLoggedIn,
AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? currentSearchQuery.substring(1) searchUser || searchHash ? currentSearchQuery.substring(1)
: currentSearchQuery); : currentSearchQuery,
searchUser ? "user" : (searchHash ? "hashtag" : "blended"));
suggestionAdapter.changeCursor(null);
prevSuggestionAsync.enqueue(cb);
} }
}; };
private void cancelSuggestionsAsync() { private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null) if (prevSuggestionAsync != null)
try { try {
prevSuggestionAsync.cancel(true); prevSuggestionAsync.cancel();
} catch (final Exception ignored) {} } catch (final Exception ignored) {}
} }
@ -706,7 +746,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final NavController navController = currentNavControllerLiveData.getValue(); final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putString("locationId", locationId); bundle.putLong("locationId", Long.valueOf(locationId));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
} }

View File

@ -16,7 +16,7 @@ import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder; import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
public final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> { public final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> {
private final OnNotificationClickListener notificationClickListener; private final OnNotificationClickListener notificationClickListener;

View File

@ -35,12 +35,12 @@ public final class SuggestionsAdapter extends CursorAdapter {
@Override @Override
public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) { public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) {
// i, username, fullname, type, picUrl, verified // i, username, fullname, type, query, picUrl, verified
// 0, 1 , 2 , 3 , 4 , 5 // 0, 1 , 2 , 3 , 4 , 5 , 6
final String fullName = cursor.getString(2); final String fullName = cursor.getString(2);
String username = cursor.getString(1); String username = cursor.getString(1);
String picUrl = cursor.getString(4); String picUrl = cursor.getString(5);
final boolean verified = cursor.getString(5).charAt(0) == 't'; final boolean verified = cursor.getString(6).charAt(0) == 't';
final String type = cursor.getString(3); final String type = cursor.getString(3);
SuggestionType suggestionType = null; SuggestionType suggestionType = null;
@ -50,22 +50,14 @@ public final class SuggestionsAdapter extends CursorAdapter {
Log.e(TAG, "Unknown suggestion type: " + type, e); Log.e(TAG, "Unknown suggestion type: " + type, e);
} }
if (suggestionType == null) return; if (suggestionType == null) return;
final String query; String query = cursor.getString(4);
switch (suggestionType) { switch (suggestionType) {
case TYPE_USER: case TYPE_USER:
username = '@' + username; username = '@' + username;
query = username;
break; break;
case TYPE_HASHTAG: case TYPE_HASHTAG:
username = '#' + username; username = '#' + username;
query = username;
break; break;
case TYPE_LOCATION:
query = fullName;
picUrl = "res:/" + R.drawable.ic_location;
break;
default:
return; // will never come here
} }
if (onSuggestionClickListener != null) { if (onSuggestionClickListener != null) {
@ -75,12 +67,8 @@ public final class SuggestionsAdapter extends CursorAdapter {
final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view); final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view);
binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE); binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE);
binding.tvUsername.setText(username); binding.tvUsername.setText(username);
if (suggestionType.equals(SuggestionType.TYPE_LOCATION)) {
binding.tvFullName.setVisibility(View.GONE);
} else {
binding.tvFullName.setVisibility(View.VISIBLE); binding.tvFullName.setVisibility(View.VISIBLE);
binding.tvFullName.setText(fullName); binding.tvFullName.setText(fullName);
}
binding.ivProfilePic.setImageURI(picUrl); binding.ivProfilePic.setImageURI(picUrl);
} }

View File

@ -9,8 +9,8 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.notification.NotificationArgs;
public final class NotificationViewHolder extends RecyclerView.ViewHolder { public final class NotificationViewHolder extends RecyclerView.ViewHolder {
private final ItemNotificationBinding binding; private final ItemNotificationBinding binding;

View File

@ -18,7 +18,6 @@ import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding; import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.enums.DirectItemType; import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
@ -97,7 +96,13 @@ public class DirectItemProfileViewHolder extends DirectItemViewHolder {
if (profile == null) return; if (profile == null) return;
binding.profilePic.setImageURI(profile.getProfilePicUrl()); binding.profilePic.setImageURI(profile.getProfilePicUrl());
binding.username.setText(profile.getUsername()); binding.username.setText(profile.getUsername());
binding.fullName.setText(profile.getFullName()); final String fullName = profile.getFullName();
if (!TextUtils.isEmpty(fullName)) {
binding.fullName.setVisibility(View.VISIBLE);
binding.fullName.setText(fullName);
} else {
binding.fullName.setVisibility(View.GONE);
}
binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE); binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE);
itemView.setOnClickListener(v -> openProfile(profile.getUsername())); itemView.setOnClickListener(v -> openProfile(profile.getUsername()));
} }

View File

@ -24,6 +24,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemVisual
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
@ -48,7 +49,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
if (media == null) return; if (media == null) return;
setExpiryInfo(visualMedia); setExpiryInfo(visualMedia);
setPreview(visualMedia, messageDirection); setPreview(visualMedia, messageDirection);
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) return; if (expired) return;
itemView.setOnClickListener(v -> openMedia(media)); itemView.setOnClickListener(v -> openMedia(media));
/*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null || /*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||
@ -118,7 +119,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
final RavenMediaViewMode viewMode = visualMedia.getViewMode(); final RavenMediaViewMode viewMode = visualMedia.getViewMode();
if (viewMode != RavenMediaViewMode.PERMANENT) { if (viewMode != RavenMediaViewMode.PERMANENT) {
final MediaItemType mediaType = media.getMediaType(); final MediaItemType mediaType = media.getMediaType();
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
final int info; final int info;
switch (mediaType) { switch (mediaType) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
@ -153,7 +154,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
private void setPreview(final DirectItemVisualMedia visualMedia, private void setPreview(final DirectItemVisualMedia visualMedia,
final MessageDirection messageDirection) { final MessageDirection messageDirection) {
final Media media = visualMedia.getMedia(); final Media media = visualMedia.getMedia();
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) { if (expired) {
binding.preview.setVisibility(View.GONE); binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE); binding.typeIcon.setVisibility(View.GONE);

View File

@ -1,81 +0,0 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.Nullable;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
//import awaisomereport.LogCollector;
//import static awais.instagrabber.utils.Utils.logCollector;
public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel> {
private static final String TAG = "LocationFetcher";
private final FetchListener<LocationModel> fetchListener;
private final long id;
public LocationFetcher(final long id, final FetchListener<LocationModel> fetchListener) {
// idSlug = id + "/" + slug UPDATE: slug can be ignored tbh
this.id = id;
this.fetchListener = fetchListener;
}
@Nullable
@Override
protected LocationModel doInBackground(final Void... voids) {
LocationModel result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + id + "/?__a=1")
.openConnection();
conn.setUseCaches(true);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject location = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject(Constants.EXTRAS_LOCATION);
final JSONObject timelineMedia = location.getJSONObject("edge_location_to_media");
// if (timelineMedia.has("edges")) {
// final JSONArray edges = timelineMedia.getJSONArray("edges");
// }
result = new LocationModel(
location.getLong(Constants.EXTRAS_ID),
location.getString("name"),
location.getString("blurb"),
location.getString("website"),
location.getString("profile_pic_url"),
timelineMedia.getLong("count"),
BigDecimal.valueOf(location.optDouble("lat", 0d)).toString(),
BigDecimal.valueOf(location.optDouble("lng", 0d)).toString()
);
}
conn.disconnect();
} catch (final Exception e) {
// if (logCollector != null)
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_LOCATION_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
}
return result;
}
@Override
protected void onPostExecute(final LocationModel result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -4,7 +4,7 @@ import java.util.List;
import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.LocationModel; import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.GraphQLService;
@ -14,12 +14,12 @@ import awais.instagrabber.webservices.ServiceCallback;
public class LocationPostFetchService implements PostFetcher.PostFetchService { public class LocationPostFetchService implements PostFetcher.PostFetchService {
private final LocationService locationService; private final LocationService locationService;
private final GraphQLService graphQLService; private final GraphQLService graphQLService;
private final LocationModel locationModel; private final Location locationModel;
private String nextMaxId; private String nextMaxId;
private boolean moreAvailable; private boolean moreAvailable;
private final boolean isLoggedIn; private final boolean isLoggedIn;
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) {
this.locationModel = locationModel; this.locationModel = locationModel;
this.isLoggedIn = isLoggedIn; this.isLoggedIn = isLoggedIn;
locationService = isLoggedIn ? LocationService.getInstance() : null; locationService = isLoggedIn ? LocationService.getInstance() : null;
@ -47,8 +47,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
} }
} }
}; };
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb);
else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb); else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb);
} }
@Override @Override

View File

@ -1,113 +0,0 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.UrlEncoder;
public final class SuggestionsFetcher extends AsyncTask<String, String, SuggestionModel[]> {
private final FetchListener<SuggestionModel[]> fetchListener;
public SuggestionsFetcher(final FetchListener<SuggestionModel[]> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
@Override
protected SuggestionModel[] doInBackground(final String... params) {
SuggestionModel[] result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/web/search/topsearch/?context=blended&count=50&query="
+ UrlEncoder.encodeUrl(params[0])).openConnection();
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn));
conn.disconnect();
final JSONArray usersArray = jsonObject.getJSONArray("users");
final JSONArray hashtagsArray = jsonObject.getJSONArray("hashtags");
final JSONArray placesArray = jsonObject.getJSONArray("places");
final int usersLen = usersArray.length();
final int hashtagsLen = hashtagsArray.length();
final int placesLen = placesArray.length();
final ArrayList<SuggestionModel> suggestionModels = new ArrayList<>(usersLen + hashtagsLen);
for (int i = 0; i < hashtagsLen; i++) {
final JSONObject hashtagsArrayJSONObject = hashtagsArray.getJSONObject(i);
final JSONObject hashtag = hashtagsArrayJSONObject.getJSONObject("hashtag");
suggestionModels.add(new SuggestionModel(false,
hashtag.getString(Constants.EXTRAS_NAME),
null,
hashtag.optString("profile_pic_url", Constants.DEFAULT_HASH_TAG_PIC),
SuggestionType.TYPE_HASHTAG,
hashtagsArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
for (int i = 0; i < placesLen; i++) {
final JSONObject placesArrayJSONObject = placesArray.getJSONObject(i);
final JSONObject place = placesArrayJSONObject.getJSONObject("place");
// name
suggestionModels.add(new SuggestionModel(false,
place.getJSONObject("location").getString("pk"), // +"/"+place.getString("slug"),
place.getString("title"),
place.optString("profile_pic_url"),
SuggestionType.TYPE_LOCATION,
placesArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
for (int i = 0; i < usersLen; i++) {
final JSONObject usersArrayJSONObject = usersArray.getJSONObject(i);
final JSONObject user = usersArrayJSONObject.getJSONObject(Constants.EXTRAS_USER);
suggestionModels.add(new SuggestionModel(user.getBoolean("is_verified"),
user.getString(Constants.EXTRAS_USERNAME),
user.getString("full_name"),
user.getString("profile_pic_url"),
SuggestionType.TYPE_USER,
usersArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
suggestionModels.trimToSize();
Collections.sort(suggestionModels);
result = suggestionModels.toArray(new SuggestionModel[0]);
}
} catch (final Exception e) {
if (BuildConfig.DEBUG && !(e instanceof InterruptedIOException)) Log.e("AWAISKING_APP", "", e);
}
return result;
}
@Override
protected void onPostExecute(final SuggestionModel[] result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -1,6 +1,7 @@
package awais.instagrabber.customviews.masoudss_waveform; package awais.instagrabber.customviews.masoudss_waveform;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapShader; import android.graphics.BitmapShader;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -50,8 +51,21 @@ public final class WaveformSeekBar extends View {
public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
this.waveBackgroundColor = context.getResources().getColor(R.color.white); final TypedArray a = context.getTheme().obtainStyledAttributes(
this.waveProgressColor = context.getResources().getColor(R.color.blue_800); attrs,
R.styleable.WaveformSeekBar,
0,
0);
final int backgroundColor;
final int progressColor;
try {
backgroundColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformBackgroundColor, R.color.white);
progressColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformProgressColor, R.color.blue_800);
} finally {
a.recycle();
}
this.waveBackgroundColor = context.getResources().getColor(backgroundColor);
this.waveProgressColor = context.getResources().getColor(progressColor);
} }
private float getSampleMax() { private float getSampleMax() {

View File

@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -17,6 +18,9 @@ public class ConfirmDialogFragment extends DialogFragment {
private Context context; private Context context;
private ConfirmDialogFragmentCallback callback; private ConfirmDialogFragmentCallback callback;
private final int defaultPositiveButtonText = R.string.ok;
// private final int defaultNegativeButtonText = R.string.cancel;
@NonNull @NonNull
public static ConfirmDialogFragment newInstance(final int requestCode, public static ConfirmDialogFragment newInstance(final int requestCode,
@StringRes final int title, @StringRes final int title,
@ -26,11 +30,21 @@ public class ConfirmDialogFragment extends DialogFragment {
@StringRes final int neutralText) { @StringRes final int neutralText) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt("requestCode", requestCode); args.putInt("requestCode", requestCode);
if (title != 0) {
args.putInt("title", title); args.putInt("title", title);
}
if (message != 0) {
args.putInt("message", message); args.putInt("message", message);
}
if (positiveText != 0) {
args.putInt("positive", positiveText); args.putInt("positive", positiveText);
}
if (negativeText != 0) {
args.putInt("negative", negativeText); args.putInt("negative", negativeText);
}
if (neutralText != 0) {
args.putInt("neutral", neutralText); args.putInt("neutral", neutralText);
}
ConfirmDialogFragment fragment = new ConfirmDialogFragment(); ConfirmDialogFragment fragment = new ConfirmDialogFragment();
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
@ -41,10 +55,9 @@ public class ConfirmDialogFragment extends DialogFragment {
@Override @Override
public void onAttach(@NonNull final Context context) { public void onAttach(@NonNull final Context context) {
super.onAttach(context); super.onAttach(context);
try { final Fragment parentFragment = getParentFragment();
callback = (ConfirmDialogFragmentCallback) getParentFragment(); if (parentFragment instanceof ConfirmDialogFragmentCallback) {
} catch (ClassCastException e) { callback = (ConfirmDialogFragmentCallback) parentFragment;
throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface");
} }
this.context = context; this.context = context;
} }
@ -53,38 +66,42 @@ public class ConfirmDialogFragment extends DialogFragment {
@Override @Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
final Bundle arguments = getArguments(); final Bundle arguments = getArguments();
int title = -1; int title = 0;
int message = -1; int message = 0;
int positiveButtonText = R.string.ok; int neutralButtonText = 0;
int negativeButtonText = R.string.cancel; int negativeButtonText = 0;
int neutralButtonText = -1;
final int positiveButtonText;
final int requestCode; final int requestCode;
if (arguments != null) { if (arguments != null) {
title = arguments.getInt("title", -1); title = arguments.getInt("title", 0);
message = arguments.getInt("message", -1); message = arguments.getInt("message", 0);
positiveButtonText = arguments.getInt("positive", R.string.ok); positiveButtonText = arguments.getInt("positive", defaultPositiveButtonText);
negativeButtonText = arguments.getInt("negative", R.string.cancel); negativeButtonText = arguments.getInt("negative", 0);
neutralButtonText = arguments.getInt("neutral", -1); neutralButtonText = arguments.getInt("neutral", 0);
requestCode = arguments.getInt("requestCode", 0); requestCode = arguments.getInt("requestCode", 0);
} else { } else {
requestCode = 0; requestCode = 0;
positiveButtonText = defaultPositiveButtonText;
} }
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
.setPositiveButton(positiveButtonText, (d, w) -> { .setPositiveButton(positiveButtonText, (d, w) -> {
if (callback == null) return; if (callback == null) return;
callback.onPositiveButtonClicked(requestCode); callback.onPositiveButtonClicked(requestCode);
}) });
.setNegativeButton(negativeButtonText, (dialog, which) -> { if (title != 0) {
builder.setTitle(title);
}
if (message != 0) {
builder.setMessage(message);
}
if (negativeButtonText != 0) {
builder.setNegativeButton(negativeButtonText, (dialog, which) -> {
if (callback == null) return; if (callback == null) return;
callback.onNegativeButtonClicked(requestCode); callback.onNegativeButtonClicked(requestCode);
}); });
if (title > 0) {
builder.setTitle(title);
} }
if (message > 0) { if (neutralButtonText != 0) {
builder.setMessage(message);
}
if (neutralButtonText > 0) {
builder.setNeutralButton(neutralButtonText, (dialog, which) -> { builder.setNeutralButton(neutralButtonText, (dialog, which) -> {
if (callback == null) return; if (callback == null) return;
callback.onNeutralButtonClicked(requestCode); callback.onNeutralButtonClicked(requestCode);

View File

@ -292,7 +292,7 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
else if (item.getItemId() == R.id.delete) { else if (item.getItemId() == R.id.delete) {
final Context context = getContext(); final Context context = getContext();
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(R.string.edit_collection) .setTitle(R.string.delete_collection)
.setMessage(R.string.delete_collection_note) .setMessage(R.string.delete_collection_note)
.setPositiveButton(R.string.confirm, (d, w) -> { .setPositiveButton(R.string.confirm, (d, w) -> {
collectionService.deleteCollection( collectionService.deleteCollection(

View File

@ -2,7 +2,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -57,7 +56,6 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
private FollowAdapter adapter; private FollowAdapter adapter;
private View.OnClickListener clickListener; private View.OnClickListener clickListener;
private FragmentFollowersViewerBinding binding; private FragmentFollowersViewerBinding binding;
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
private SwipeRefreshLayout root; private SwipeRefreshLayout root;
private FriendshipService friendshipService; private FriendshipService friendshipService;
private AppCompatActivity fragmentActivity; private AppCompatActivity fragmentActivity;

View File

@ -3,7 +3,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
@ -95,7 +94,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private Hashtag hashtagModel = null; private Hashtag hashtagModel = null;
private ActionMode actionMode; private ActionMode actionMode;
private StoriesService storiesService; private StoriesService storiesService;
private AsyncTask<?, ?, ?> currentlyExecuting;
private boolean isLoggedIn; private boolean isLoggedIn;
private TagsService tagsService; private TagsService tagsService;
private GraphQLService graphQLService; private GraphQLService graphQLService;
@ -474,7 +472,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtag, hashtag,
FavoriteType.HASHTAG, FavoriteType.HASHTAG,
hashtagModel.getName(), hashtagModel.getName(),
hashtagModel.getProfilePicUrl(), "res:/" + R.drawable.ic_hashtag,
result.getDateAdded() result.getDateAdded()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -518,7 +516,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtag, hashtag,
FavoriteType.HASHTAG, FavoriteType.HASHTAG,
hashtagModel.getName(), hashtagModel.getName(),
hashtagModel.getProfilePicUrl(), "res:/" + R.drawable.ic_hashtag,
new Date() new Date()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -533,7 +531,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
}); });
} }
})); }));
hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getProfilePicUrl()); hashtagDetailsBinding.mainHashtagImage.setImageURI("res:/" + R.drawable.ic_hashtag);
final String postCount = String.valueOf(hashtagModel.getMediaCount()); final String postCount = String.valueOf(hashtagModel.getMediaCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
hashtagModel.getMediaCount() > 2000000000L hashtagModel.getMediaCount() > 2000000000L

View File

@ -3,14 +3,9 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.SpannableStringBuilder;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Log; import android.util.Log;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -45,7 +40,6 @@ import java.util.Set;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.LocationFetcher;
import awais.instagrabber.asyncs.LocationPostFetchService; import awais.instagrabber.asyncs.LocationPostFetchService;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback;
@ -56,23 +50,24 @@ import awais.instagrabber.db.entities.Favorite;
import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.FavoriteRepository;
import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.StoriesService;
//import awaisomereport.LogCollector; //import awaisomereport.LogCollector;
import static androidx.core.content.PermissionChecker.checkSelfPermission; import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
//import static awais.instagrabber.utils.Utils.logCollector; //import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -89,10 +84,11 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private boolean hasStories = false; private boolean hasStories = false;
private boolean opening = false; private boolean opening = false;
private long locationId; private long locationId;
private LocationModel locationModel; private Location locationModel;
private ActionMode actionMode; private ActionMode actionMode;
private StoriesService storiesService; private StoriesService storiesService;
private AsyncTask<?, ?, ?> currentlyExecuting; private GraphQLService graphQLService;
private LocationService locationService;
private boolean isLoggedIn; private boolean isLoggedIn;
private boolean storiesFetching; private boolean storiesFetching;
private Set<Media> selectedFeedModels; private Set<Media> selectedFeedModels;
@ -265,12 +261,29 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
} }
}; };
private final ServiceCallback<Location> cb = new ServiceCallback<Location>() {
@Override
public void onSuccess(final Location result) {
locationModel = result;
binding.swipeRefreshLayout.setRefreshing(false);
setupLocationDetails();
}
@Override
public void onFailure(final Throwable t) {
setupLocationDetails();
}
};
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity(); fragmentActivity = (MainActivity) requireActivity();
final String cookie = settingsHelper.getString(Constants.COOKIE);
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
locationService = isLoggedIn ? LocationService.getInstance() : null;
storiesService = StoriesService.getInstance(null, 0L, null); storiesService = StoriesService.getInstance(null, 0L, null);
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -354,8 +367,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private void init() { private void init() {
if (getArguments() == null) return; if (getArguments() == null) return;
final String cookie = settingsHelper.getString(Constants.COOKIE);
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments()); final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments());
locationId = fragmentArgs.getLocationId(); locationId = fragmentArgs.getLocationId();
locationDetailsBinding.favChip.setVisibility(View.GONE); locationDetailsBinding.favChip.setVisibility(View.GONE);
@ -377,42 +388,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
private void fetchLocationModel() { private void fetchLocationModel() {
stopCurrentExecutor();
binding.swipeRefreshLayout.setRefreshing(true); binding.swipeRefreshLayout.setRefreshing(true);
currentlyExecuting = new LocationFetcher(locationId, result -> { if (isLoggedIn) locationService.fetch(locationId, cb);
locationModel = result; else graphQLService.fetchLocation(locationId, cb);
binding.swipeRefreshLayout.setRefreshing(false);
if (locationModel == null) {
final Context context = getContext();
if (context == null) return;
Toast.makeText(context, R.string.error_loading_location, Toast.LENGTH_SHORT).show();
binding.swipeRefreshLayout.setEnabled(false);
return;
}
setTitle();
setupLocationDetails();
setupPosts();
fetchStories();
// fetchPosts();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void setupLocationDetails() { private void setupLocationDetails() {
final long locationId = locationModel.getId(); if (locationModel == null) {
try {
Toast.makeText(getContext(), R.string.error_loading_location, Toast.LENGTH_SHORT).show();
binding.swipeRefreshLayout.setEnabled(false);
}
catch (Exception ignored) {}
return;
}
setTitle();
setupPosts();
fetchStories();
final long locationId = locationModel.getPk();
// binding.swipeRefreshLayout.setRefreshing(true); // binding.swipeRefreshLayout.setRefreshing(true);
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location);
final String postCount = String.valueOf(locationModel.getPostCount()); // final String postCount = String.valueOf(locationModel.getCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, // final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
locationModel.getPostCount() > 2000000000L // locationModel.getPostCount() > 2000000000L
? 2000000000 // ? 2000000000
: locationModel.getPostCount().intValue(), // : locationModel.getPostCount().intValue(),
postCount)); // postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); // span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); // span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
locationDetailsBinding.mainLocPostCount.setText(span); // locationDetailsBinding.mainLocPostCount.setText(span);
locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); // locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE);
locationDetailsBinding.locationFullName.setText(locationModel.getName()); locationDetailsBinding.locationFullName.setText(locationModel.getName());
CharSequence biography = locationModel.getBio(); CharSequence biography = locationModel.getAddress() + "\n" + locationModel.getCity();
// binding.locationBiography.setCaptionIsExpandable(true); // binding.locationBiography.setCaptionIsExpandable(true);
// binding.locationBiography.setCaptionIsExpanded(true); // binding.locationBiography.setCaptionIsExpanded(true);
@ -423,22 +430,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} else { } else {
locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE);
locationDetailsBinding.locationBiography.setText(biography); locationDetailsBinding.locationBiography.setText(biography);
locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { // locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this); // final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle(); // final Bundle bundle = new Bundle();
final String originalText = autoLinkItem.getOriginalText().trim(); // final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText); // bundle.putString(ARG_HASHTAG, originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle); // navController.navigate(R.id.action_global_hashTagFragment, bundle);
}); // });
locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { // locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText().trim(); // final String originalText = autoLinkItem.getOriginalText().trim();
navigateToProfile(originalText); // navigateToProfile(originalText);
}); // });
locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, // locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,
autoLinkItem.getOriginalText() // autoLinkItem.getOriginalText()
.trim())); // .trim()));
locationDetailsBinding.locationBiography // locationDetailsBinding.locationBiography
.addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); // .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));
locationDetailsBinding.locationBiography.setOnLongClickListener(v -> { locationDetailsBinding.locationBiography.setOnLongClickListener(v -> {
Utils.copyText(context, biography); Utils.copyText(context, biography);
return true; return true;
@ -457,16 +464,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
locationDetailsBinding.btnMap.setOnClickListener(null); locationDetailsBinding.btnMap.setOnClickListener(null);
} }
final String url = locationModel.getUrl();
if (TextUtils.isEmpty(url)) {
locationDetailsBinding.locationUrl.setVisibility(View.GONE);
} else if (!url.startsWith("http")) {
locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE);
locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl("http://" + url));
} else {
locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE);
locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url));
}
final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context);
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
locationDetailsBinding.favChip.setVisibility(View.VISIBLE); locationDetailsBinding.favChip.setVisibility(View.VISIBLE);
@ -481,7 +478,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId), String.valueOf(locationId),
FavoriteType.LOCATION, FavoriteType.LOCATION,
locationModel.getName(), locationModel.getName(),
locationModel.getSdProfilePic(), "res:/" + R.drawable.ic_location,
result.getDateAdded() result.getDateAdded()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -523,7 +520,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId), String.valueOf(locationId),
FavoriteType.LOCATION, FavoriteType.LOCATION,
locationModel.getName(), locationModel.getName(),
locationModel.getSdProfilePic(), "res:/" + R.drawable.ic_location,
new Date() new Date()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -581,18 +578,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
} }
private void stopCurrentExecutor() {
if (currentlyExecuting != null) {
try {
currentlyExecuting.cancel(true);
} catch (final Exception e) {
// if (logCollector != null) logCollector.appendException(
// e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
Log.e(TAG, "", e);
}
}
}
private void setTitle() { private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null && locationModel != null) { if (actionBar != null && locationModel != null) {

View File

@ -2,7 +2,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@ -37,9 +36,9 @@ import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.NotificationImage; import awais.instagrabber.repositories.responses.notification.NotificationImage;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;

View File

@ -79,7 +79,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
public void onHighlightClick(final HighlightModel model, final int position) { public void onHighlightClick(final HighlightModel model, final int position) {
if (model == null) return; if (model == null) return;
final NavDirections action = StoryListViewerFragmentDirections final NavDirections action = StoryListViewerFragmentDirections
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(position)); .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(model.getId()));
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
} }

View File

@ -65,6 +65,8 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
@ -304,11 +306,18 @@ public class StoryViewerFragment extends Fragment {
// isNotification = fragmentArgs.getIsNotification(); // isNotification = fragmentArgs.getIsNotification();
final Type type = options.getType(); final Type type = options.getType();
if (currentFeedStoryIndex >= 0) { if (currentFeedStoryIndex >= 0) {
viewModel = type == Type.HIGHLIGHT switch (type) {
? type == Type.STORY_ARCHIVE case HIGHLIGHT:
? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class) viewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class);
: new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class) break;
: new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); case STORY_ARCHIVE:
viewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class);
break;
default:
case FEED_STORY_POSITION:
viewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
break;
}
} }
setupStories(); setupStories();
} }
@ -728,9 +737,9 @@ public class StoryViewerFragment extends Fragment {
return; return;
} }
final HighlightModel model = models.get(currentFeedStoryIndex); final HighlightModel model = models.get(currentFeedStoryIndex);
currentStoryMediaId = model.getId(); currentStoryMediaId = parseStoryMediaId(model.getId());
currentStoryUsername = model.getTitle(); currentStoryUsername = model.getTitle();
fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); fetchOptions = StoryViewerOptions.forStoryArchive(model.getId());
break; break;
} }
} }
@ -1139,4 +1148,20 @@ public class StoryViewerFragment extends Fragment {
resetView(); resetView();
} }
} }
/**
* Parses the Story's media ID. For user stories this is a number, but for archive stories
* this is "archiveDay:" plus a number.
*/
private static String parseStoryMediaId(String rawId) {
final String regex = "(?:archiveDay:)?(.+)";
final Pattern pattern = Pattern.compile(regex);
final Matcher matcher = pattern.matcher(rawId);
if (matcher.matches() && matcher.groupCount() >= 1) {
return matcher.group(1);
}
return rawId;
}
} }

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.dm_inbox_menu, menu);
pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests"); pendingRequestsMenuItem = menu.findItem(R.id.pending_requests);
pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24) pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached);
.setVisible(false)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue());
} }
@Override @Override
@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
@SuppressLint("UnsafeExperimentalUsageError") @SuppressLint("UnsafeExperimentalUsageError")
private void attachPendingRequestsBadge(@Nullable final Integer count) { private void attachPendingRequestsBadge(@Nullable final Integer count) {
if (pendingRequestsMenuItem == null) return; if (pendingRequestsMenuItem == null) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
attachPendingRequestsBadge(count);
}
}, 500);
return;
}
if (pendingRequestTotalBadgeDrawable == null) { if (pendingRequestTotalBadgeDrawable == null) {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;

View File

@ -140,6 +140,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
viewModel.isViewerAdmin().observe(getViewLifecycleOwner(), this::setApprovalRelatedUI); viewModel.isViewerAdmin().observe(getViewLifecycleOwner(), this::setApprovalRelatedUI);
viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required)); viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests); viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
viewModel.isGroup().observe(getViewLifecycleOwner(), this::setupSettings);
final NavController navController = NavHostFragment.findNavController(this); final NavController navController = NavHostFragment.findNavController(this);
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry(); final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
if (backStackEntry != null) { if (backStackEntry != null) {
@ -186,7 +187,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
R.string.admin_approval_required_description, R.string.admin_approval_required_description,
R.string.ok, R.string.ok,
R.string.cancel, R.string.cancel,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog");
return; return;
@ -207,13 +208,11 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
} }
private void init() { private void init() {
setupSettings(); // setupSettings();
setupMembers(); setupMembers();
} }
private void setupSettings() { private void setupSettings(final boolean isGroup) {
Boolean isGroup = viewModel.isGroup().getValue();
if (isGroup == null) isGroup = false;
binding.groupSettings.setVisibility(isGroup ? View.VISIBLE : View.GONE); binding.groupSettings.setVisibility(isGroup ? View.VISIBLE : View.GONE);
binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle()); binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> { binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
@ -272,10 +271,10 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
LEAVE_THREAD_REQUEST_CODE, LEAVE_THREAD_REQUEST_CODE,
R.string.dms_action_leave_question, R.string.dms_action_leave_question,
-1, 0,
R.string.yes, R.string.yes,
R.string.no, R.string.no,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog");
}); });
@ -290,7 +289,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
R.string.dms_action_end_description, R.string.dms_action_end_description,
R.string.yes, R.string.yes,
R.string.no, R.string.no,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog");
}); });

View File

@ -1227,7 +1227,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
if (!isEmojiPickerShown) { if (!isEmojiPickerShown) {
binding.emojiPicker.setAlpha(0); binding.emojiPicker.setAlpha(0);
} }
imm.showSoftInput(binding.input, InputMethodManager.SHOW_IMPLICIT); final boolean shown = imm.showSoftInput(binding.input, InputMethodManager.SHOW_IMPLICIT);
if (!shown) {
Log.e(TAG, "showKeyboard: System did not display the keyboard");
}
if (!isEmojiPickerShown) { if (!isEmojiPickerShown) {
animatePan(keyboardHeight); animatePan(keyboardHeight);
} }
@ -1328,95 +1331,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
} }
// private void sendText(final String text, final String itemId, final boolean delete) {
// DirectThreadBroadcaster.TextBroadcastOptions textOptions = null;
// DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null;
// if (text != null) {
// try {
// textOptions = new DirectThreadBroadcaster.TextBroadcastOptions(text);
// } catch (UnsupportedEncodingException e) {
// Log.e(TAG, "Error", e);
// return;
// }
// } else {
// reactionOptions = new DirectThreadBroadcaster.ReactionBroadcastOptions(itemId, delete);
// }
// broadcast(text != null ? textOptions : reactionOptions, result -> {
// final Context context = getContext();
// if (context == null) return;
// if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) {
// Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// return;
// }
// if (text != null) {
// // binding.commentText.setText("");
// } else {
// // final View viewWithTag = binding.messageList.findViewWithTag(directItemModel);
// // if (viewWithTag != null) {
// // final ViewParent dim = viewWithTag.getParent();
// // if (dim instanceof View) {
// // final View dimView = (View) dim;
// // final View likedContainer = dimView.findViewById(R.id.liked_container);
// // if (likedContainer != null) {
// // likedContainer.setVisibility(delete ? View.GONE : View.VISIBLE);
// // }
// // }
// // }
// // directItemModel.setLiked();
// }
// context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM));
// hasSentSomething = true;
// // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener)
// // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// });
// }
// private void sendImage(final Uri imageUri) {
// final Context context = getContext();
// if (context == null) return;
// try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) {
// final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
// Toast.makeText(context, R.string.uploading, Toast.LENGTH_SHORT).show();
// // Upload Image
// final ImageUploader imageUploader = new ImageUploader();
// imageUploader.setOnTaskCompleteListener(response -> {
// if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) {
// Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// if (response != null && response.getResponse() != null) {
// Log.e(TAG, response.getResponse().toString());
// }
// return;
// }
// final JSONObject responseJson = response.getResponse();
// try {
// final String uploadId = responseJson.getString("upload_id");
// // Broadcast
// final DirectThreadBroadcaster.ImageBroadcastOptions options = new DirectThreadBroadcaster.ImageBroadcastOptions(true, uploadId);
// hasSentSomething = true;
// broadcast(options,
// broadcastResponse -> {
// // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener)
// // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// });
// } catch (JSONException e) {
// Log.e(TAG, "Error parsing json response", e);
// }
// });
// final ImageUploadOptions options = ImageUploadOptions.builder(bitmap).build();
// imageUploader.execute(options);
// } catch (IOException e) {
// Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// Log.e(TAG, "Error opening file", e);
// }
// }
// private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions,
// final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) {
// final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId);
// broadcaster.setOnTaskCompleteListener(listener);
// broadcaster.execute(broadcastOptions);
// }
@NonNull @NonNull
private User getUser(final long userId) { private User getUser(final long userId) {
for (final User user : users) { for (final User user : users) {
@ -1426,58 +1340,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
return null; return null;
} }
// private void searchUsername(final String text) {
// final Bundle bundle = new Bundle();
// bundle.putString("username", "@" + text);
// NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle);
// }
// class ThreadAction extends AsyncTask<String, Void, Void> {
// String action, argument;
//
// protected Void doInBackground(String... rawAction) {
// action = rawAction[0];
// argument = rawAction[1];
// final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + argument + "/" + action + "/";
// try {
// String urlParameters = "_csrftoken=" + COOKIE.split("csrftoken=")[1].split(";")[0]
// + "&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID);
// final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
// urlConnection.setRequestMethod("POST");
// urlConnection.setUseCaches(false);
// urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
// urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
// urlConnection.setDoOutput(true);
// DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
// wr.writeBytes(urlParameters);
// wr.flush();
// wr.close();
// urlConnection.connect();
// if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// if (action.equals("delete")) {
// hasDeletedSomething = true;
// } else if (action.equals("seen")) {
// // context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM));
// }
// }
// urlConnection.disconnect();
// } catch (Throwable ex) {
// Log.e("austin_debug", action + ": " + ex);
// }
// return null;
// }
//
// @Override
// protected void onPostExecute(Void result) {
// if (hasDeletedSomething) {
// // directItemModel = null;
// // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener)
// // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// }
// }
// }
private void setupKbHeightProvider() { private void setupKbHeightProvider() {
if (heightProvider != null) return; if (heightProvider != null) return;
heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> { heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> {

View File

@ -588,10 +588,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
binding.swipeRefreshLayout.setEnabled(false); binding.swipeRefreshLayout.setEnabled(false);
binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24); binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24);
binding.privatePage2.setText(R.string.no_acc); binding.privatePage2.setText(R.string.no_acc);
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams();
layoutParams.topMargin = 0;
layoutParams.gravity = Gravity.CENTER;
binding.privatePage.setLayoutParams(layoutParams);
binding.privatePage.setVisibility(View.VISIBLE); binding.privatePage.setVisibility(View.VISIBLE);
return; return;
} }
@ -683,6 +679,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
binding.postsRecyclerView.refresh(); binding.postsRecyclerView.refresh();
} }
profileDetailsBinding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); profileDetailsBinding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
profileDetailsBinding.isPrivate.setVisibility(profileModel.isPrivate() ? View.VISIBLE : View.GONE);
final long profileId = profileModel.getPk(); final long profileId = profileModel.getPk();
if (isLoggedIn) { if (isLoggedIn) {
fetchStoryAndHighlights(profileId); fetchStoryAndHighlights(profileId);
@ -917,11 +914,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} else { } else {
profileDetailsBinding.mainFollowers.setClickable(false); profileDetailsBinding.mainFollowers.setClickable(false);
profileDetailsBinding.mainFollowing.setClickable(false); profileDetailsBinding.mainFollowing.setClickable(false);
// error
binding.privatePage1.setImageResource(R.drawable.lock); binding.privatePage1.setImageResource(R.drawable.lock);
binding.privatePage2.setText(R.string.priv_acc); binding.privatePage2.setText(R.string.priv_acc);
binding.privatePage.setVisibility(View.VISIBLE); binding.privatePage.setVisibility(View.VISIBLE);
binding.postsRecyclerView.setVisibility(View.GONE); binding.postsRecyclerView.setVisibility(View.GONE);
binding.swipeRefreshLayout.setRefreshing(false);
} }
} }
@ -1206,7 +1203,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
private void updateSwipeRefreshState() { private void updateSwipeRefreshState() {
Log.d("austin_debug", "usrs: " + binding.postsRecyclerView.isFetching());
binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching()); binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching());
} }

View File

@ -9,6 +9,8 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreferenceCompat; import androidx.preference.SwitchPreferenceCompat;
import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
@ -29,6 +31,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment {
} }
screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getUpdateCheckPreference(context));
screen.addPreference(getFlagSecurePreference(context)); screen.addPreference(getFlagSecurePreference(context));
final List<Preference> preferences = FlavorSettings.getInstance().getPreferences(context,
getChildFragmentManager(),
SettingCategory.GENERAL);
if (preferences != null) {
for (final Preference preference : preferences) {
screen.addPreference(preference);
}
}
} }
private Preference getDefaultTabPreference(@NonNull final Context context) { private Preference getDefaultTabPreference(@NonNull final Context context) {

View File

@ -0,0 +1,14 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import java.util.List;
public interface IFlavorSettings {
List<Preference> getPreferences(Context context,
FragmentManager childFragmentManager,
SettingCategory settingCategory);
}

View File

@ -5,4 +5,5 @@ public final class PreferenceKeys {
public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh";
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit";
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number";
public static final String PREF_ENABLE_SENTRY = "enable_sentry";
} }

View File

@ -0,0 +1,6 @@
package awais.instagrabber.fragments.settings;
public enum SettingCategory {
GENERAL,
// add more as and when required
}

View File

@ -179,6 +179,9 @@ public final class ThreadManager {
return null; return null;
} }
final DirectInbox inbox = inboxResource.data; final DirectInbox inbox = inboxResource.data;
if (inbox == null) {
return null;
}
final List<DirectThread> threads = inbox.getThreads(); final List<DirectThread> threads = inbox.getThreads();
if (threads == null || threads.isEmpty()) { if (threads == null || threads.isEmpty()) {
return null; return null;
@ -264,7 +267,10 @@ public final class ThreadManager {
} }
private List<User> getUsersWithCurrentUser(final DirectThread t) { private List<User> getUsersWithCurrentUser(final DirectThread t) {
final ImmutableList.Builder<User> builder = ImmutableList.<User>builder().add(currentUser); final ImmutableList.Builder<User> builder = ImmutableList.builder();
if (currentUser != null) {
builder.add(currentUser);
}
final List<User> users = t.getUsers(); final List<User> users = t.getUsers();
if (users != null) { if (users != null) {
builder.addAll(users); builder.addAll(users);

View File

@ -1,56 +0,0 @@
package awais.instagrabber.models;
import java.io.Serializable;
public final class LocationModel implements Serializable {
private final long postCount;
private final long id;
private final String name;
private final String bio;
private final String url;
private final String sdProfilePic;
private final String lat;
private final String lng;
public LocationModel(final long id,
final String name,
final String bio,
final String url,
final String sdProfilePic,
final long postCount,
final String lat,
final String lng) {
this.id = id;
this.name = name;
this.bio = bio;
this.url = url;
this.sdProfilePic = sdProfilePic;
this.postCount = postCount;
this.lat = lat;
this.lng = lng;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getBio() {
return bio;
}
public String getUrl() {
return url;
}
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; }
public String getSdProfilePic() {
return sdProfilePic;
}
public Long getPostCount() { return postCount; }
}

View File

@ -1,51 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import awais.instagrabber.models.enums.SuggestionType;
public final class SuggestionModel implements Comparable<SuggestionModel> {
private final int position;
private final boolean isVerified;
private final String username, name, profilePic;
private final SuggestionType suggestionType;
public SuggestionModel(final boolean isVerified, final String username, final String name, final String profilePic,
final SuggestionType suggestionType, final int position) {
this.isVerified = isVerified;
this.username = username;
this.name = name;
this.profilePic = profilePic;
this.suggestionType = suggestionType;
this.position = position;
}
public boolean isVerified() {
return isVerified;
}
public String getUsername() {
return username;
}
public String getName() {
return name;
}
public String getProfilePic() {
return profilePic;
}
public SuggestionType getSuggestionType() {
return suggestionType;
}
public int getPosition() {
return position;
}
@Override
public int compareTo(@NonNull final SuggestionModel model) {
return Integer.compare(getPosition(), model.getPosition());
}
}

View File

@ -2,7 +2,7 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.repositories.responses.FeedFetchResponse; import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.FieldMap; import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View File

@ -16,4 +16,7 @@ public interface GraphQLRepository {
@GET("/explore/tags/{tag}/?__a=1") @GET("/explore/tags/{tag}/?__a=1")
Call<String> getTag(@Path("tag") String tag); Call<String> getTag(@Path("tag") String tag);
@GET("/explore/locations/{locationId}/?__a=1")
Call<String> getLocation(@Path("locationId") long locationId);
} }

View File

@ -3,12 +3,15 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.repositories.responses.LocationFeedResponse; import awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;
public interface LocationRepository { public interface LocationRepository {
@GET("/api/v1/locations/{location}/info/")
Call<Place> fetch(@Path("location") final long locationId);
@GET("/api/v1/feed/location/{location}/") @GET("/api/v1/feed/location/{location}/")
Call<LocationFeedResponse> fetchPosts(@Path("location") final long locationId, Call<LocationFeedResponse> fetchPosts(@Path("location") final long locationId,

View File

@ -0,0 +1,15 @@
package awais.instagrabber.repositories;
import java.util.Map;
import awais.instagrabber.repositories.responses.search.SearchResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
public interface SearchRepository {
@GET
Call<SearchResponse> search(@Url String url, @QueryMap(encoded = true) Map<String, String> queryParams);
}

View File

@ -57,8 +57,8 @@ public class StoryViewerOptions implements Serializable {
return new StoryViewerOptions(position, Type.FEED_STORY_POSITION); return new StoryViewerOptions(position, Type.FEED_STORY_POSITION);
} }
public static StoryViewerOptions forStoryArchive(final int position) { public static StoryViewerOptions forStoryArchive(final String id) {
return new StoryViewerOptions(position, Type.STORY_ARCHIVE); return new StoryViewerOptions(id, Type.STORY_ARCHIVE);
} }
public long getId() { public long getId() {

View File

@ -5,22 +5,22 @@ import java.io.Serializable;
import awais.instagrabber.models.enums.FollowingType; import awais.instagrabber.models.enums.FollowingType;
public final class Hashtag implements Serializable { public final class Hashtag implements Serializable {
private final FollowingType following; // 0 false 1 true private final FollowingType following; // 0 false 1 true; not on search results
private final long mediaCount; private final long mediaCount;
private final String id; private final String id;
private final String name; private final String name;
private final String profilePicUrl; // on app API this is always null (property exists) private final String searchResultSubtitle; // shows how many posts there are on search results
public Hashtag(final String id, public Hashtag(final String id,
final String name, final String name,
final String profilePicUrl,
final long mediaCount, final long mediaCount,
final FollowingType following) { final FollowingType following,
final String searchResultSubtitle) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.profilePicUrl = profilePicUrl;
this.mediaCount = mediaCount; this.mediaCount = mediaCount;
this.following = following; this.following = following;
this.searchResultSubtitle = searchResultSubtitle;
} }
public String getId() { public String getId() {
@ -31,10 +31,6 @@ public final class Hashtag implements Serializable {
return name; return name;
} }
public String getProfilePicUrl() {
return profilePicUrl;
}
public Long getMediaCount() { public Long getMediaCount() {
return mediaCount; return mediaCount;
} }
@ -42,4 +38,8 @@ public final class Hashtag implements Serializable {
public FollowingType getFollowing() { public FollowingType getFollowing() {
return following; return following;
} }
public String getSubtitle() {
return searchResultSubtitle;
}
} }

View File

@ -9,16 +9,16 @@ public class Location implements Serializable {
private final String name; private final String name;
private final String address; private final String address;
private final String city; private final String city;
private final float lng; private final double lng;
private final float lat; private final double lat;
public Location(final long pk, public Location(final long pk,
final String shortName, final String shortName,
final String name, final String name,
final String address, final String address,
final String city, final String city,
final float lng, final double lng,
final float lat) { final double lat) {
this.pk = pk; this.pk = pk;
this.shortName = shortName; this.shortName = shortName;
this.name = name; this.name = name;
@ -48,22 +48,24 @@ public class Location implements Serializable {
return city; return city;
} }
public float getLng() { public double getLng() {
return lng; return lng;
} }
public float getLat() { public double getLat() {
return lat; return lat;
} }
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; }
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
final Location location = (Location) o; final Location location = (Location) o;
return pk == location.pk && return pk == location.pk &&
Float.compare(location.lng, lng) == 0 && Double.compare(location.lng, lng) == 0 &&
Float.compare(location.lat, lat) == 0 && Double.compare(location.lat, lat) == 0 &&
Objects.equals(shortName, location.shortName) && Objects.equals(shortName, location.shortName) &&
Objects.equals(name, location.name) && Objects.equals(name, location.name) &&
Objects.equals(address, location.address) && Objects.equals(address, location.address) &&

View File

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public class Media implements Serializable { public class Media implements Serializable {

View File

@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
public class NewsInboxResponse { public class NewsInboxResponse {
private final NotificationCounts counts; private final NotificationCounts counts;
private final List<Notification> newStories; private final List<Notification> newStories;

View File

@ -0,0 +1,43 @@
package awais.instagrabber.repositories.responses;
public class Place {
private final Location location;
// for search
private final String title; // those are repeated within location
private final String subtitle; // address
private final String slug; // browser only; for end of address
// for location info
private final String status;
public Place(final Location location,
final String title,
final String subtitle,
final String slug,
final String status) {
this.location = location;
this.title = title;
this.subtitle = subtitle;
this.slug = slug;
this.status = status;
}
public Location getLocation() {
return location;
}
public String getTitle() {
return title;
}
public String getSubtitle() {
return subtitle;
}
public String getSlug() {
return slug;
}
public String getStatus() {
return status;
}
}

View File

@ -44,7 +44,7 @@ public class DirectItemActionLog implements Serializable {
return Objects.hash(description, bold, textAttributes); return Objects.hash(description, bold, textAttributes);
} }
public static class TextRange { public static class TextRange implements Serializable {
private final int start; private final int start;
private final int end; private final int end;
private final String color; private final String color;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;

View File

@ -1,9 +1,11 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.repositories.responses.Media;
public class EndOfFeedGroup implements Serializable { public class EndOfFeedGroup implements Serializable {
private final String id; private final String id;
private final String title; private final String title;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;

View File

@ -1,7 +1,9 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.Media;
public class FeedFetchResponse { public class FeedFetchResponse {
private final List<Media> items; private final List<Media> items;
private final int numResults; private final int numResults;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -7,8 +7,6 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import android.util.Log;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public class NotificationArgs { public class NotificationArgs {

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
public class NotificationImage { public class NotificationImage {
private final String id; private final String id;

View File

@ -0,0 +1,38 @@
package awais.instagrabber.repositories.responses.search;
import awais.instagrabber.repositories.responses.Hashtag;
import awais.instagrabber.repositories.responses.Place;
import awais.instagrabber.repositories.responses.User;
public class SearchItem {
private final User user;
private final Place place;
private final Hashtag hashtag;
private final int position;
public SearchItem(final User user,
final Place place,
final Hashtag hashtag,
final int position) {
this.user = user;
this.place = place;
this.hashtag = hashtag;
this.position = position;
}
public User getUser() {
return user;
}
public Place getPlace() {
return place;
}
public Hashtag getHashtag() {
return hashtag;
}
public int getPosition() {
return position;
}
}

View File

@ -0,0 +1,48 @@
package awais.instagrabber.repositories.responses.search;
import java.util.List;
import awais.instagrabber.repositories.responses.User;
public class SearchResponse {
// app
private final List<SearchItem> list;
// browser
private final List<SearchItem> users;
private final List<SearchItem> places;
private final List<SearchItem> hashtags;
// universal
private final String status;
public SearchResponse(final List<SearchItem> list,
final List<SearchItem> users,
final List<SearchItem> places,
final List<SearchItem> hashtags,
final String status) {
this.list = list;
this.users = users;
this.places = places;
this.hashtags = hashtags;
this.status = status;
}
public List<SearchItem> getList() {
return list;
}
public List<SearchItem> getUsers() {
return users;
}
public List<SearchItem> getPlaces() {
return places;
}
public List<SearchItem> getHashtags() {
return hashtags;
}
public String getStatus() {
return status;
}
}

View File

@ -18,13 +18,11 @@ import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.repositories.responses.NotificationCounts; import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ActivityCheckerService extends Service { public class ActivityCheckerService extends Service {
private static final String TAG = "ActivityCheckerService"; private static final String TAG = "ActivityCheckerService";
private static final int INITIAL_DELAY_MILLIS = 200; private static final int INITIAL_DELAY_MILLIS = 200;

View File

@ -1,6 +1,8 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
public final class Constants { public final class Constants {
public static final String CRASH_REPORT_EMAIL = "barinsta@austinhuang.me";
// string prefs // string prefs
public static final String FOLDER_PATH = "custom_path"; public static final String FOLDER_PATH = "custom_path";
public static final String DATE_TIME_FORMAT = "date_time_format"; public static final String DATE_TIME_FORMAT = "date_time_format";

View File

@ -1,10 +1,8 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
import android.Manifest; import android.Manifest;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
@ -13,8 +11,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.work.Constraints; import androidx.work.Constraints;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.NetworkType; import androidx.work.NetworkType;
@ -294,7 +290,8 @@ public final class DownloadUtils {
final int childPositionIfSingle) { final int childPositionIfSingle) {
final Map<String, String> map = new HashMap<>(); final Map<String, String> map = new HashMap<>();
for (final Media media : feedModels) { for (final Media media : feedModels) {
final File downloadDir = getDownloadDir(context, "@" + media.getUser().getUsername()); final User mediaUser = media.getUser();
final File downloadDir = getDownloadDir(context, mediaUser == null ? "" : "@" + mediaUser.getUsername());
if (downloadDir == null) return; if (downloadDir == null) return;
switch (media.getMediaType()) { switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
@ -307,9 +304,8 @@ public final class DownloadUtils {
case MEDIA_TYPE_VOICE: { case MEDIA_TYPE_VOICE: {
final String url = getUrlOfType(media); final String url = getUrlOfType(media);
String fileName = media.getId(); String fileName = media.getId();
final User user = media.getUser(); if (mediaUser != null) {
if (user != null) { fileName = mediaUser.getUsername() + "_" + fileName;
fileName = user.getUsername() + "_" + fileName;
} }
final File file = getDownloadSaveFile(downloadDir, fileName, url); final File file = getDownloadSaveFile(downloadDir, fileName, url);
map.put(url, file.getAbsolutePath()); map.put(url, file.getAbsolutePath());

View File

@ -729,8 +729,11 @@ public final class ResponseBodyUtils {
width = dimensions.optInt("width"); width = dimensions.optInt("width");
} }
String thumbnailUrl = null; String thumbnailUrl = null;
final JSONArray displayResources = feedItem.getJSONArray("display_resources");
final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>(); final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>();
if (feedItem.has("display_resources") || feedItem.has("thumbnail_resources")) {
final JSONArray displayResources = feedItem.has("display_resources")
? feedItem.getJSONArray("display_resources")
: feedItem.getJSONArray("thumbnail_resources");
for (int i = 0; i < displayResources.length(); i++) { for (int i = 0; i < displayResources.length(); i++) {
final JSONObject displayResource = displayResources.getJSONObject(i); final JSONObject displayResource = displayResources.getJSONObject(i);
candidates.add(new MediaCandidate( candidates.add(new MediaCandidate(
@ -739,6 +742,7 @@ public final class ResponseBodyUtils {
displayResource.getString("src") displayResource.getString("src")
)); ));
} }
}
final ImageVersions2 imageVersions2 = new ImageVersions2(candidates); final ImageVersions2 imageVersions2 = new ImageVersions2(candidates);
User user = backup; User user = backup;
@ -943,8 +947,7 @@ public final class ResponseBodyUtils {
// } // }
public static StoryModel parseStoryItem(final JSONObject data, public static StoryModel parseStoryItem(final JSONObject data,
final boolean isLoc, final boolean isLocOrHashtag,
final boolean isHashtag,
final String username) throws JSONException { final String username) throws JSONException {
final boolean isVideo = data.has("video_duration"); final boolean isVideo = data.has("video_duration");
final StoryModel model = new StoryModel(data.getString("id"), final StoryModel model = new StoryModel(data.getString("id"),
@ -952,9 +955,7 @@ public final class ResponseBodyUtils {
.getString("url"), null, .getString("url"), null,
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0), data.optLong("taken_at", 0),
(isLoc || isHashtag) isLocOrHashtag ? data.getJSONObject("user").getString("username") : username,
? data.getJSONObject("user").getString("username")
: username,
data.getJSONObject("user").getLong("pk"), data.getJSONObject("user").getLong("pk"),
data.optBoolean("can_reply")); data.optBoolean("can_reply"));

View File

@ -15,6 +15,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;
import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_LANGUAGE;
import static awais.instagrabber.utils.Constants.APP_THEME; import static awais.instagrabber.utils.Constants.APP_THEME;
import static awais.instagrabber.utils.Constants.APP_UA; import static awais.instagrabber.utils.Constants.APP_UA;
@ -145,6 +146,10 @@ public final class SettingsHelper {
if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply(); if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply();
} }
public boolean hasPreference(final String key) {
return sharedPreferences != null && sharedPreferences.contains(key);
}
@StringDef( @StringDef(
{APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION,
CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME,
@ -156,7 +161,7 @@ public final class SettingsHelper {
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY,
CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH,
FLAG_SECURE, TOGGLE_KEYWORD_FILTER}) FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY})
public @interface BooleanSettings {} public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER})

View File

@ -69,7 +69,7 @@ public final class TextUtils {
str = str.trim(); str = str.trim();
return "".equals(str) || "null".equals(str) || str.isEmpty(); return "".equals(str) || "null".equals(str) || str.isEmpty();
} }
return "null".contentEquals(charSequence) || "".contentEquals(charSequence) || charSequence.length() < 1; return "null".contentEquals(charSequence) || "".contentEquals(charSequence);
} }
public static String millisToTimeString(final long millis) { public static String millisToTimeString(final long millis) {

View File

@ -1,7 +1,10 @@
package awais.instagrabber.utils.emoji; package awais.instagrabber.utils.emoji;
import android.content.Context;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -18,6 +21,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import awais.instagrabber.R;
import awais.instagrabber.customviews.emoji.Emoji; import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.customviews.emoji.EmojiCategory; import awais.instagrabber.customviews.emoji.EmojiCategory;
import awais.instagrabber.customviews.emoji.EmojiCategoryType; import awais.instagrabber.customviews.emoji.EmojiCategoryType;
@ -33,25 +37,25 @@ public final class EmojiParser {
private Map<EmojiCategoryType, EmojiCategory> categoryMap = Collections.emptyMap(); private Map<EmojiCategoryType, EmojiCategory> categoryMap = Collections.emptyMap();
private ImmutableList<EmojiCategory> categories; private ImmutableList<EmojiCategory> categories;
public static EmojiParser getInstance() { public static void setup(@NonNull final Context context) {
if (instance == null) { if (instance == null) {
synchronized (LOCK) { synchronized (LOCK) {
if (instance == null) { if (instance == null) {
instance = new EmojiParser(); instance = new EmojiParser(context);
} }
} }
} }
}
public static EmojiParser getInstance() {
if (instance == null) {
throw new RuntimeException("Setup not done!");
}
return instance; return instance;
} }
private EmojiParser() { private EmojiParser(final Context context) {
final String file = "res/raw/emojis.json"; try (final InputStream in = context.getResources().openRawResource(R.raw.emojis)) {
final ClassLoader classLoader = getClass().getClassLoader();
if (classLoader == null) {
Log.e(TAG, "Emoji: classLoader is null");
return;
}
try (final InputStream in = classLoader.getResourceAsStream(file)) {
final String json = NetworkUtils.readFromInputStream(in); final String json = NetworkUtils.readFromInputStream(in);
final Gson gson = new GsonBuilder() final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)

View File

@ -181,6 +181,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
MediaUtils.getVoiceInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener<MediaUtils.VideoInfo>() { MediaUtils.getVoiceInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener<MediaUtils.VideoInfo>() {
@Override @Override
public void onLoad(@Nullable final MediaUtils.VideoInfo videoInfo) { public void onLoad(@Nullable final MediaUtils.VideoInfo videoInfo) {
if (videoInfo == null) return;
threadManager.sendVoice(data, threadManager.sendVoice(data,
uri, uri,
result.getWaveform(), result.getWaveform(),

View File

@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
public class NotificationViewModel extends ViewModel { public class NotificationViewModel extends ViewModel {
private MutableLiveData<List<Notification>> list; private MutableLiveData<List<Notification>> list;

View File

@ -12,10 +12,10 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository; import awais.instagrabber.repositories.FeedRepository;
import awais.instagrabber.repositories.responses.EndOfFeedDemarcator; import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.EndOfFeedGroup; import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.EndOfFeedGroupSet; import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.FeedFetchResponse; import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;

View File

@ -19,6 +19,7 @@ import awais.instagrabber.repositories.GraphQLRepository;
import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse;
import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Hashtag;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
@ -392,9 +393,53 @@ public class GraphQLService extends BaseService {
callback.onSuccess(new Hashtag( callback.onSuccess(new Hashtag(
body.getString(Constants.EXTRAS_ID), body.getString(Constants.EXTRAS_ID),
body.getString("name"), body.getString("name"),
body.getString("profile_pic_url"),
timelineMedia.getLong("count"), timelineMedia.getLong("count"),
body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING)); body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING,
null));
} catch (JSONException e) {
Log.e(TAG, "onResponse", e);
if (callback != null) {
callback.onFailure(e);
}
}
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
if (callback != null) {
callback.onFailure(t);
}
}
});
}
public void fetchLocation(final long locationId,
final ServiceCallback<Location> callback) {
final Call<String> request = repository.getLocation(locationId);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
final String rawBody = response.body();
if (rawBody == null) {
Log.e(TAG, "Error occurred while fetching gql location of " + locationId);
callback.onSuccess(null);
return;
}
try {
final JSONObject body = new JSONObject(rawBody)
.getJSONObject("graphql")
.getJSONObject(Constants.EXTRAS_LOCATION);
final JSONObject timelineMedia = body.getJSONObject("edge_location_to_media");
final JSONObject address = new JSONObject(body.getString("address_json"));
callback.onSuccess(new Location(
body.getLong(Constants.EXTRAS_ID),
body.getString("slug"),
body.getString("name"),
address.optString("street_address"),
address.optString("city_name"),
body.optDouble("lng", 0d),
body.optDouble("lat", 0d)
));
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "onResponse", e); Log.e(TAG, "onResponse", e);
if (callback != null) { if (callback != null) {

View File

@ -5,7 +5,9 @@ import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.LocationRepository; import awais.instagrabber.repositories.LocationRepository;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.LocationFeedResponse; import awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
@ -67,34 +69,24 @@ public class LocationService extends BaseService {
}); });
} }
// private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { public void fetch(@NonNull final long locationId,
// final JSONObject root = new JSONObject(body); final ServiceCallback<Location> callback) {
// final boolean moreAvailable = root.optBoolean("more_available"); final Call<Place> request = repository.fetch(locationId);
// final String nextMaxId = root.optString("next_max_id"); request.enqueue(new Callback<Place>() {
// final JSONArray itemsJson = root.optJSONArray("items"); @Override
// final List<FeedModel> items = parseItems(itemsJson); public void onResponse(@NonNull final Call<Place> call, @NonNull final Response<Place> response) {
// return new PostsFetchResponse( if (callback == null) {
// items, return;
// moreAvailable, }
// nextMaxId callback.onSuccess(response.body() == null ? null : response.body().getLocation());
// ); }
// }
@Override
// private List<FeedModel> parseItems(final JSONArray items) throws JSONException { public void onFailure(@NonNull final Call<Place> call, @NonNull final Throwable t) {
// if (items == null) { if (callback != null) {
// return Collections.emptyList(); callback.onFailure(t);
// } }
// final List<FeedModel> feedModels = new ArrayList<>(); }
// for (int i = 0; i < items.length(); i++) { });
// final JSONObject itemJson = items.optJSONObject(i); }
// if (itemJson == null) {
// continue;
// }
// final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson);
// if (feedModel != null) {
// feedModels.add(feedModel);
// }
// }
// return feedModels;
// }
} }

View File

@ -16,6 +16,10 @@ import awais.instagrabber.repositories.responses.NewsInboxResponse;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.NotificationArgs;
import awais.instagrabber.repositories.responses.NotificationCounts; import awais.instagrabber.repositories.responses.NotificationCounts;
import awais.instagrabber.repositories.responses.NewsInboxResponse;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.UserSearchResponse; import awais.instagrabber.repositories.responses.UserSearchResponse;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;

View File

@ -0,0 +1,50 @@
package awais.instagrabber.webservices;
import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.SearchRepository;
import awais.instagrabber.repositories.responses.search.SearchResponse;
import awais.instagrabber.utils.TextUtils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class SearchService extends BaseService {
private static final String TAG = "LocationService";
private final SearchRepository repository;
private static SearchService instance;
private SearchService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://www.instagram.com")
.build();
repository = retrofit.create(SearchRepository.class);
}
public static SearchService getInstance() {
if (instance == null) {
instance = new SearchService();
}
return instance;
}
public Call<SearchResponse> search(final boolean isLoggedIn,
final String query,
final String context) {
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
builder.put("query", query);
// context is one of: "blended", "user", "place", "hashtag"
// note that "place" and "hashtag" can contain ONE user result, who knows why
builder.put("context", context);
builder.put("count", "50");
return repository.search(isLoggedIn
? "https://i.instagram.com/api/v1/fbsearch/topsearch_flat/"
: "https://www.instagram.com/web/search/topsearch/",
builder.build());
}
}

View File

@ -93,7 +93,7 @@ public class StoriesService extends BaseService {
} }
try { try {
final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0); final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0);
callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, false, null)); callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, null));
} catch (JSONException e) { } catch (JSONException e) {
callback.onFailure(e); callback.onFailure(e);
} }
@ -185,7 +185,7 @@ public class StoriesService extends BaseService {
final boolean isBestie = node.optBoolean("has_besties_media", false); final boolean isBestie = node.optBoolean("has_besties_media", false);
StoryModel firstStoryModel = null; StoryModel firstStoryModel = null;
if (itemJson != null) { if (itemJson != null) {
firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, false, null); firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null);
} }
feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie)); feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie));
} catch (Exception e) {} // to cover promotional reels with non-long user pk's } catch (Exception e) {} // to cover promotional reels with non-long user pk's
@ -361,9 +361,8 @@ public class StoriesService extends BaseService {
final ServiceCallback<List<StoryModel>> callback) { final ServiceCallback<List<StoryModel>> callback) {
final String url = buildUrl(options); final String url = buildUrl(options);
final Call<String> userStoryCall = repository.getUserStory(url); final Call<String> userStoryCall = repository.getUserStory(url);
final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION; final boolean isLocOrHashtag = options.getType() == StoryViewerOptions.Type.LOCATION || options.getType() == StoryViewerOptions.Type.HASHTAG;
final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG; final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT || options.getType() == StoryViewerOptions.Type.STORY_ARCHIVE;
final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT;
userStoryCall.enqueue(new Callback<String>() { userStoryCall.enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
@ -377,7 +376,7 @@ public class StoriesService extends BaseService {
data = new JSONObject(body); data = new JSONObject(body);
if (!isHighlight) { if (!isHighlight) {
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel"); data = data.optJSONObject((isLocOrHashtag) ? "story" : "reel");
} else { } else {
data = data.getJSONObject("reels").optJSONObject(options.getName()); data = data.getJSONObject("reels").optJSONObject(options.getName());
} }
@ -385,8 +384,7 @@ public class StoriesService extends BaseService {
String username = null; String username = null;
if (data != null if (data != null
// && localUsername == null // && localUsername == null
&& !isLoc && !isLocOrHashtag) {
&& !isHashtag) {
username = data.getJSONObject("user").getString("username"); username = data.getJSONObject("user").getString("username");
} }
@ -394,12 +392,11 @@ public class StoriesService extends BaseService {
if (data != null if (data != null
&& (media = data.optJSONArray("items")) != null && (media = data.optJSONArray("items")) != null
&& media.length() > 0 && media.optJSONObject(0) != null) { && media.length() > 0 && media.optJSONObject(0) != null) {
final int mediaLen = media.length(); final int mediaLen = media.length();
final List<StoryModel> models = new ArrayList<>(); final List<StoryModel> models = new ArrayList<>();
for (int i = 0; i < mediaLen; ++i) { for (int i = 0; i < mediaLen; ++i) {
data = media.getJSONObject(i); data = media.getJSONObject(i);
models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, username)); models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username));
} }
callback.onSuccess(models); callback.onSuccess(models);
} else { } else {
@ -540,6 +537,7 @@ public class StoriesService extends BaseService {
id = String.valueOf(options.getId()); id = String.valueOf(options.getId());
break; break;
case HIGHLIGHT: case HIGHLIGHT:
case STORY_ARCHIVE:
builder.append("feed/reels_media/?user_ids="); builder.append("feed/reels_media/?user_ids=");
id = options.getName(); id = options.getName();
break; break;
@ -547,15 +545,12 @@ public class StoriesService extends BaseService {
break; break;
// case FEED_STORY_POSITION: // case FEED_STORY_POSITION:
// break; // break;
// case STORY_ARCHIVE:
// break;
} }
if (id == null) { if (id == null) {
return null; return null;
} }
final String userId = id.replace(":", "%3A"); builder.append(id);
builder.append(userId); if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) {
if (type != StoryViewerOptions.Type.HIGHLIGHT) {
builder.append("/story/"); builder.append("/story/");
} }
return builder.toString(); return builder.toString();

View File

@ -1,50 +1,35 @@
package awaisomereport; package awaisomereport;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.utils.Utils;
public final class CrashReporter implements Thread.UncaughtExceptionHandler { public final class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final String TAG = CrashReporter.class.getSimpleName();
private static CrashReporter reporterInstance; private static CrashReporter reporterInstance;
private final Application application;
private final String email;
// private final File crashLogsZip; // private final File crashLogsZip;
private final CrashHandler crashHandler;
private boolean startAttempted = false; private boolean startAttempted = false;
private Thread.UncaughtExceptionHandler defaultEH;
public static CrashReporter get(final Application application) { public static CrashReporter get(final Application application) {
if (reporterInstance == null) reporterInstance = new CrashReporter(application); if (reporterInstance == null) {
reporterInstance = new CrashReporter(application);
}
return reporterInstance; return reporterInstance;
} }
private CrashReporter(@NonNull final Application application) { private CrashReporter(@NonNull final Application application) {
this.application = application; crashHandler = new CrashHandler(application);
this.email = "barinsta@austinhuang.me";
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); // this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
} }
public void start() { public void start() {
if (!startAttempted) { if (!startAttempted) {
defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this); Thread.setDefaultUncaughtExceptionHandler(this);
startAttempted = true; startAttempted = true;
} }
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
@Override @Override
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) {
final StringBuilder reportBuilder = new StringBuilder(); if (crashHandler == null) {
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues."); defaultEH.uncaughtException(t, exception);
reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation."); return;
reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString()); }
crashHandler.uncaughtException(t, exception, defaultEH);
reportBuilder
.append("\r\n\r\nInformation:\r\n==============")
.append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME)
.append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE)
.append("\r\nPHONE-MODEL : ").append(Build.MODEL)
.append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE)
.append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT)
.append("\r\nBRAND : ").append(Build.BRAND)
.append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER)
.append("\r\nBOARD : ").append(Build.BOARD)
.append("\r\nDEVICE : ").append(Build.DEVICE)
.append("\r\nPRODUCT : ").append(Build.PRODUCT)
.append("\r\nHOST : ").append(Build.HOST)
.append("\r\nTAGS : ").append(Build.TAGS);
reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n");
final Writer result = new StringWriter();
try (final PrintWriter printWriter = new PrintWriter(result)) {
exception.printStackTrace(printWriter);
reportBuilder.append(result.toString());
reportBuilder.append("\r\nCause:\r\n==============");
// for AsyncTask crashes
Throwable cause = exception.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
reportBuilder.append(result.toString());
cause = cause.getCause();
}
}
reportBuilder.append("\r\n\r\n**** End of current Report ***");
final String errorContent = reportBuilder.toString();
try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) {
trace.write(errorContent.getBytes());
} catch (final Exception ex) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", ex);
}
application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
// zipLogs();
Process.killProcess(Process.myPid());
System.exit(10);
}
// public synchronized CrashReporter zipLogs() {
// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() :
// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs");
//
// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip);
// final ZipOutputStream zos = new ZipOutputStream(fos)) {
//
// final File[] files = logDir.listFiles();
//
// if (files != null) {
// zos.setLevel(5);
// byte[] buffer;
// for (final File file : files) {
// if (file != null && file.length() > 0) {
// buffer = new byte[1024];
// try (final FileInputStream fis = new FileInputStream(file)) {
// zos.putNextEntry(new ZipEntry(file.getName()));
// int length;
// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length);
// zos.closeEntry();
// }
// }
// }
// }
//
// } catch (final Exception e) {
// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
// }
//
// return this;
// }
@SuppressWarnings("ResultOfMethodCallIgnored")
public void startCrashEmailIntent(final Context context) {
try {
final String filePath = context.getFilesDir().getAbsolutePath();
String[] errorFileList;
try {
final File dir = new File(filePath);
if (dir.exists() && !dir.isDirectory()) dir.delete();
dir.mkdir();
errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace"));
} catch (final Exception e) {
errorFileList = null;
}
if (errorFileList != null && errorFileList.length > 0) {
final StringBuilder errorStringBuilder;
errorStringBuilder = new StringBuilder("\r\n\r\n");
final int maxSendMail = 5;
int curIndex = 0;
for (final String curString : errorFileList) {
final File file = new File(filePath + '/' + curString);
if (curIndex++ <= maxSendMail) {
errorStringBuilder.append("New Trace collected:\r\n=====================\r\n");
try (final BufferedReader input = new BufferedReader(new FileReader(file))) {
String line;
while ((line = input.readLine()) != null)
errorStringBuilder.append(line).append("\r\n");
}
}
file.delete();
}
errorStringBuilder.append("\r\n\r\n");
context.startActivity(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("message/rfc822")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.putExtra(Intent.EXTRA_EMAIL, new String[]{email})
// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip))
.putExtra(Intent.EXTRA_SUBJECT, "Barinsta Crash Report")
.putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), "Select an email app to send crash logs"));
}
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
} }
} }

View File

@ -0,0 +1,134 @@
package awaisomereport;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.utils.Constants;
public final class CrashReporterHelper {
private static final String TAG = CrashReporterHelper.class.getSimpleName();
public static void startErrorReporterActivity(@NonNull final Application application,
@NonNull final Throwable exception) {
final StringBuilder reportBuilder = new StringBuilder();
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues.")
.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.")
.append("\r\n\r\nError report collected on: ").append(new Date().toString())
.append("\r\n\r\nInformation:\r\n==============")
.append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME)
.append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE)
.append("\r\nPHONE-MODEL : ").append(Build.MODEL)
.append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE)
.append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT)
.append("\r\nBRAND : ").append(Build.BRAND)
.append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER)
.append("\r\nBOARD : ").append(Build.BOARD)
.append("\r\nDEVICE : ").append(Build.DEVICE)
.append("\r\nPRODUCT : ").append(Build.PRODUCT)
.append("\r\nHOST : ").append(Build.HOST)
.append("\r\nTAGS : ").append(Build.TAGS);
reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n");
final Writer result = new StringWriter();
try (final PrintWriter printWriter = new PrintWriter(result)) {
exception.printStackTrace(printWriter);
reportBuilder.append(result.toString());
reportBuilder.append("\r\nCause:\r\n==============");
// for AsyncTask crashes
Throwable cause = exception.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
reportBuilder.append(result.toString());
cause = cause.getCause();
}
}
reportBuilder.append("\r\n\r\n**** End of current Report ***");
final String errorContent = reportBuilder.toString();
try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) {
trace.write(errorContent.getBytes());
} catch (final Exception ex) {
if (BuildConfig.DEBUG) Log.e(TAG, "", ex);
}
application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public static void startCrashEmailIntent(final Context context) {
try {
final String filePath = context.getFilesDir().getAbsolutePath();
String[] errorFileList;
try {
final File dir = new File(filePath);
if (dir.exists() && !dir.isDirectory()) {
//noinspection ResultOfMethodCallIgnored
dir.delete();
}
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace"));
} catch (final Exception e) {
errorFileList = null;
}
if (errorFileList == null || errorFileList.length <= 0) {
return;
}
final StringBuilder errorStringBuilder;
errorStringBuilder = new StringBuilder("\r\n\r\n");
final int maxSendMail = 5;
int curIndex = 0;
for (final String curString : errorFileList) {
final File file = new File(filePath + '/' + curString);
if (curIndex++ <= maxSendMail) {
errorStringBuilder.append("New Trace collected:\r\n=====================\r\n");
try (final BufferedReader input = new BufferedReader(new FileReader(file))) {
String line;
while ((line = input.readLine()) != null)
errorStringBuilder.append(line).append("\r\n");
}
}
//noinspection ResultOfMethodCallIgnored
file.delete();
}
errorStringBuilder.append("\r\n\r\n");
final Resources resources = context.getResources();
context.startActivity(Intent.createChooser(
new Intent(Intent.ACTION_SEND)
.setType("message/rfc822")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.putExtra(Intent.EXTRA_EMAIL, new String[]{Constants.CRASH_REPORT_EMAIL})
// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip))
.putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.crash_report_subject))
.putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()),
context.getResources().getString(R.string.crash_report_title))
);
} catch (final Exception e) {
Log.e(TAG, "", e);
}
}
}

View File

@ -43,8 +43,9 @@ public final class ErrorReporterActivity extends Activity implements View.OnClic
@Override @Override
public void onClick(@NonNull final View v) { public void onClick(@NonNull final View v) {
if (v == btnReport) if (v == btnReport) {
CrashReporter.get(getApplication()).startCrashEmailIntent(this); CrashReporterHelper.startCrashEmailIntent(this);
}
finish(); finish();
System.exit(10); System.exit(10);
} }

View File

@ -0,0 +1,7 @@
package awaisomereport;
public interface ICrashHandler {
void uncaughtException(Thread t,
Throwable exception,
Thread.UncaughtExceptionHandler defaultEH);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -156,7 +156,7 @@
android:hint="@string/message" android:hint="@string/message"
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingBottom="12dp" android:paddingBottom="12dp"
android:textColor="@color/white" android:textColor="?dmInputTextColor"
android:textColorHint="@color/grey_500" android:textColorHint="@color/grey_500"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@ -50,8 +50,8 @@
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/privatePage1" android:id="@+id/privatePage1"
android:layout_width="@dimen/private_page_margins" android:layout_width="@dimen/private_page_size"
android:layout_height="@dimen/private_page_margins" android:layout_height="@dimen/private_page_size"
app:srcCompat="@drawable/lock" /> app:srcCompat="@drawable/lock" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView

View File

@ -9,8 +9,7 @@
android:id="@+id/story_container" android:id="@+id/story_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" app:layout_constraintBottom_toTopOf="@id/postActions"
app:layout_constraintBottom_toTopOf="@id/storiesList"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -37,11 +36,17 @@
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone" /> android:visibility="gone" />
</FrameLayout>
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/postActions" android:id="@+id/postActions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
app:layout_constraintTop_toBottomOf="@id/story_container"
app:layout_constraintBottom_toTopOf="@id/storiesList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="#0000"> android:background="#0000">
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -123,7 +128,6 @@
android:visibility="gone" android:visibility="gone"
app:backgroundTint="@color/btn_green_background" /> app:backgroundTint="@color/btn_green_background" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</FrameLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnBackward" android:id="@+id/btnBackward"
@ -144,7 +148,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/story_container" app:layout_constraintTop_toBottomOf="@id/postActions"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnForward" app:layout_constraintEnd_toStartOf="@id/btnForward"
app:layout_constraintStart_toEndOf="@id/btnBackward" /> app:layout_constraintStart_toEndOf="@id/btnBackward" />

View File

@ -113,6 +113,7 @@
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/white"
app:layout_constraintBottom_toTopOf="@id/chat_message_layout" app:layout_constraintBottom_toTopOf="@id/chat_message_layout"
app:layout_constraintEnd_toEndOf="@id/chat_message_layout" app:layout_constraintEnd_toEndOf="@id/chat_message_layout"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"

View File

@ -15,6 +15,7 @@
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:paddingBottom="@dimen/dm_message_card_radius_small" android:paddingBottom="@dimen/dm_message_card_radius_small"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/white"
tools:text="Some message" /> tools:text="Some message" />
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
@ -35,6 +36,7 @@
android:paddingTop="@dimen/dm_message_card_radius_small" android:paddingTop="@dimen/dm_message_card_radius_small"
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
tools:text="Title of the website" /> tools:text="Title of the website" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
@ -46,6 +48,7 @@
android:paddingStart="@dimen/dm_message_card_radius" android:paddingStart="@dimen/dm_message_card_radius"
android:paddingTop="@dimen/dm_message_card_radius_small" android:paddingTop="@dimen/dm_message_card_radius_small"
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:textColor="@color/white"
tools:text="Some summary of the website" /> tools:text="Some summary of the website" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
@ -59,5 +62,6 @@
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:paddingBottom="@dimen/dm_message_card_radius_small" android:paddingBottom="@dimen/dm_message_card_radius_small"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:textColor="@color/white"
tools:text="test.com" /> tools:text="test.com" />
</LinearLayout> </LinearLayout>

View File

@ -86,6 +86,7 @@
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingBottom="0dp" android:paddingBottom="0dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/caption" app:layout_constraintBottom_toTopOf="@id/caption"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -101,6 +102,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" android:padding="8dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@ -27,7 +27,7 @@
android:ellipsize="end" android:ellipsize="end"
android:gravity="bottom" android:gravity="bottom"
android:singleLine="true" android:singleLine="true"
android:textColor="?android:textColorPrimary" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
@ -62,12 +62,14 @@
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/barrier" app:layout_constraintBottom_toTopOf="@id/barrier"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/username" app:layout_constraintStart_toStartOf="@id/username"
app:layout_constraintTop_toBottomOf="@id/username" app:layout_constraintTop_toBottomOf="@id/username"
tools:text="Full name" /> tools:text="Full name"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Barrier <androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier" android:id="@+id/barrier"

View File

@ -78,6 +78,7 @@
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:padding="8dp" android:padding="8dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/preview" app:layout_constraintTop_toBottomOf="@id/preview"

View File

@ -41,5 +41,6 @@
android:layout_gravity="start|fill_horizontal" android:layout_gravity="start|fill_horizontal"
android:padding="8dp" android:padding="8dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="@color/white"
tools:text="Some text " /> tools:text="Some text " />
</LinearLayout> </LinearLayout>

View File

@ -9,4 +9,5 @@
android:paddingEnd="@dimen/dm_message_card_radius" android:paddingEnd="@dimen/dm_message_card_radius"
android:paddingBottom="@dimen/dm_message_card_radius_small" android:paddingBottom="@dimen/dm_message_card_radius_small"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/white"
tools:text="Text message" /> tools:text="Text message" />

View File

@ -66,10 +66,12 @@
android:layout_height="54dp" android:layout_height="54dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
app:waveformBackgroundColor="?dmWaveformBgColor"
app:layout_constraintBottom_toTopOf="@id/duration" app:layout_constraintBottom_toTopOf="@id/duration"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/play_wrapper" app:layout_constraintStart_toEndOf="@id/play_wrapper"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:waveformProgressColor="?dmWaveformProgressColor" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/duration" android:id="@+id/duration"

View File

@ -15,25 +15,25 @@
android:layout_height="@dimen/profile_picture_size" android:layout_height="@dimen/profile_picture_size"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
app:actualImageScaleType="centerCrop" app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toStartOf="@id/mainLocPostCount" app:layout_constraintEnd_toStartOf="@id/btnMap"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher" /> tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView <!-- <androidx.appcompat.widget.AppCompatTextView-->
android:id="@+id/mainLocPostCount" <!-- android:id="@+id/mainLocPostCount"-->
android:layout_width="0dp" <!-- android:layout_width="0dp"-->
android:layout_height="0dp" <!-- android:layout_height="0dp"-->
android:gravity="center_vertical" <!-- android:gravity="center_vertical"-->
android:maxLines="1" <!-- android:maxLines="1"-->
android:paddingStart="12dp" <!-- android:paddingStart="12dp"-->
android:paddingEnd="12dp" <!-- android:paddingEnd="12dp"-->
android:textAppearance="@style/TextAppearance.AppCompat" <!-- android:textAppearance="@style/TextAppearance.AppCompat"-->
app:layout_constraintBottom_toTopOf="@id/btnMap" <!-- app:layout_constraintBottom_toTopOf="@id/btnMap"-->
app:layout_constraintEnd_toEndOf="parent" <!-- app:layout_constraintEnd_toEndOf="parent"-->
app:layout_constraintStart_toEndOf="@id/mainLocationImage" <!-- app:layout_constraintStart_toEndOf="@id/mainLocationImage"-->
app:layout_constraintTop_toTopOf="parent" <!-- app:layout_constraintTop_toTopOf="parent"-->
tools:text="35 Posts" /> <!-- tools:text="35 Posts" />-->
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/btnMap" android:id="@+id/btnMap"
@ -44,9 +44,8 @@
app:chipBackgroundColor="@null" app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_map_24" app:chipIcon="@drawable/ic_outline_map_24"
app:chipIconTint="@color/green_500" app:chipIconTint="@color/green_500"
app:layout_constraintBottom_toTopOf="@id/locationFullName" app:layout_constraintTop_toTopOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/mainLocationImage" app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
app:rippleColor="@color/grey_500" app:rippleColor="@color/grey_500"
tools:visibility="visible" /> tools:visibility="visible" />
@ -60,8 +59,8 @@
app:chipIcon="@drawable/ic_outline_star_plus_24" app:chipIcon="@drawable/ic_outline_star_plus_24"
app:chipIconTint="@color/yellow_800" app:chipIconTint="@color/yellow_800"
app:layout_constraintBottom_toBottomOf="@id/mainLocationImage" app:layout_constraintBottom_toBottomOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/btnMap" app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount" app:layout_constraintTop_toBottomOf="@id/btnMap"
app:rippleColor="@color/yellow_400" /> app:rippleColor="@color/yellow_400" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
@ -90,27 +89,10 @@
android:padding="8dp" android:padding="8dp"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toTopOf="@id/locationUrl" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationFullName" app:layout_constraintTop_toBottomOf="@id/locationFullName"
tools:text="IN THE MIDDLE OF OUR STREET" /> tools:text="IN THE MIDDLE OF OUR STREET" />
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/locationUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/locationBiography"
android:ellipsize="marquee"
android:padding="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationBiography"
tools:text="https://austinhuang.me/"
tools:textColor="@android:color/holo_blue_dark"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -36,6 +36,7 @@
app:chipIconTint="@color/deep_purple_200" app:chipIconTint="@color/deep_purple_200"
app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainProfileImage" app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/purple_200" app:rippleColor="@color/purple_200"
tools:visibility="visible" /> tools:visibility="visible" />
@ -50,6 +51,7 @@
app:chipBackgroundColor="@null" app:chipBackgroundColor="@null"
app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/btnFollow" app:layout_constraintStart_toEndOf="@id/btnFollow"
app:layout_constraintTop_toTopOf="parent"
tools:text="omg what do u expect" tools:text="omg what do u expect"
tools:visibility="visible" /> tools:visibility="visible" />
@ -66,6 +68,7 @@
app:chipIconTint="@color/blue_700" app:chipIconTint="@color/blue_700"
app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainStatus" app:layout_constraintStart_toEndOf="@id/mainStatus"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/blue_A400" app:rippleColor="@color/blue_A400"
tools:visibility="visible" /> tools:visibility="visible" />
@ -81,6 +84,7 @@
app:chipIconTint="@color/red_600" app:chipIconTint="@color/red_600"
app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/btnSaved" app:layout_constraintStart_toEndOf="@id/btnSaved"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/red_300" app:rippleColor="@color/red_300"
tools:visibility="visible" /> tools:visibility="visible" />
@ -109,6 +113,7 @@
app:chipBackgroundColor="@null" app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_person_pin_24" app:chipIcon="@drawable/ic_outline_person_pin_24"
app:chipIconTint="@color/deep_orange_800" app:chipIconTint="@color/deep_orange_800"
app:layout_constraintBottom_toTopOf="@+id/mainFullName"
app:layout_constraintStart_toEndOf="@id/mainProfileImage" app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/fav_chip" app:layout_constraintTop_toBottomOf="@id/fav_chip"
app:rippleColor="@color/deep_orange_400" app:rippleColor="@color/deep_orange_400"
@ -124,6 +129,7 @@
app:chipBackgroundColor="@null" app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_round_send_24" app:chipIcon="@drawable/ic_round_send_24"
app:chipIconTint="@color/green" app:chipIconTint="@color/green"
app:layout_constraintBottom_toTopOf="@+id/mainFullName"
app:layout_constraintStart_toEndOf="@id/btnTagged" app:layout_constraintStart_toEndOf="@id/btnTagged"
app:layout_constraintTop_toBottomOf="@id/fav_chip" app:layout_constraintTop_toBottomOf="@id/fav_chip"
app:rippleColor="@color/green" app:rippleColor="@color/green"
@ -161,6 +167,23 @@
app:srcCompat="@drawable/verified" app:srcCompat="@drawable/verified"
tools:visibility="visible" /> tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/isPrivate"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:scaleType="fitCenter"
android:tint="@color/red_500"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/mainFullName"
app:layout_constraintStart_toEndOf="@id/isVerified"
app:layout_constraintTop_toBottomOf="@id/btnTagged"
app:srcCompat="@drawable/lock"
tools:visibility="visible" />
<CheckBox <CheckBox
android:id="@+id/fav_cb" android:id="@+id/fav_cb"
android:layout_width="wrap_content" android:layout_width="wrap_content"

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