1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-26 00:27:30 +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"
]
},
{
"login": "Zopieux",
"name": "Alexandre Macabies",
"avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
"profile": "https://github.com/Zopieux",
"contributions": [
"code"
]
},
{
"login": "MeLlamoPablo",
"name": "Pablo Rodríguez",
@ -61,10 +52,10 @@
]
},
{
"login": "AwaisKing",
"name": "AWAiS",
"avatar_url": "https://avatars3.githubusercontent.com/u/5278488",
"profile": "http://rerolledgeek.blogspot.com/",
"login": "Zopieux",
"name": "Alexandre Macabies",
"avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
"profile": "https://github.com/Zopieux",
"contributions": [
"code"
]
@ -119,16 +110,6 @@
"translation"
]
},
{
"login": "e-edgren",
"name": "Airikr",
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
"profile": "https://airikr.me/",
"contributions": [
"ideas",
"question"
]
},
{
"login": "Akrai",
"name": "Akrai",
@ -274,6 +255,15 @@
"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",
"name": "RAMAR-RAR",
@ -319,6 +309,15 @@
"translation"
]
},
{
"login": "Sitavi",
"name": "Sitavi",
"avatar_url": "https://avatars.githubusercontent.com/u/80586127?v=4",
"profile": "https://github.com/Sitavi",
"contributions": [
"translation"
]
},
{
"login": "Still34",
"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
.cxx
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://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/Zopieux"><img src="https://avatars.githubusercontent.com/u/81353?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandre Macabies</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=Zopieux" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MeLlamoPablo"><img src="https://avatars.githubusercontent.com/u/11708035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pablo Rodríguez</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=MeLlamoPablo" title="Code">💻</a></td>
<td align="center"><a href="http://rerolledgeek.blogspot.com/"><img src="https://avatars3.githubusercontent.com/u/5278488?s=100" width="100px;" alt=""/><br /><sub><b>AWAiS</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=AwaisKing" title="Code">💻</a></td>
<td align="center"><a href="https://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>
<td align="center"><a href="https://snajdovski.github.io"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/CrazyMarvin"><img src="https://avatars3.githubusercontent.com/u/15004217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CrazyMarvin</b></sub></a><br /><a href="#financial-CrazyMarvin" title="Financial">💵</a></td>
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://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/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/dimitrist19"><img src="https://avatars.githubusercontent.com/u/56406468?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dimitris T</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</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://janek.xyz/"><img src="https://avatars3.githubusercontent.com/u/8365659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Janek</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/GenosseFlosse"><img src="https://avatars.githubusercontent.com/u/59205524?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GenosseFlosse</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
<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>
<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>
</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://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://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>

View File

@ -1,5 +1,15 @@
apply plugin: 'com.android.application'
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 {
compileSdkVersion 29
@ -48,8 +58,41 @@ android {
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 {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
@ -70,7 +113,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:$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.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
@ -81,7 +124,7 @@ dependencies {
implementation 'androidx.palette:palette:1.0.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
def room_version = "2.2.6"
@ -90,7 +133,7 @@ dependencies {
annotationProcessor "androidx.room:room-compiler:$room_version"
// 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-lifecycle:$camerax_version"
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: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'
}

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

View File

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

View File

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

View File

@ -9,8 +9,8 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationArgs;
public final class NotificationViewHolder extends RecyclerView.ViewHolder {
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.LayoutDmProfileBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
@ -97,7 +96,13 @@ public class DirectItemProfileViewHolder extends DirectItemViewHolder {
if (profile == null) return;
binding.profilePic.setImageURI(profile.getProfilePicUrl());
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);
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.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
@ -48,7 +49,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
if (media == null) return;
setExpiryInfo(visualMedia);
setPreview(visualMedia, messageDirection);
final boolean expired = media.getPk() == null;
final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) return;
itemView.setOnClickListener(v -> openMedia(media));
/*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||
@ -118,7 +119,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
final RavenMediaViewMode viewMode = visualMedia.getViewMode();
if (viewMode != RavenMediaViewMode.PERMANENT) {
final MediaItemType mediaType = media.getMediaType();
final boolean expired = media.getPk() == null;
final boolean expired = TextUtils.isEmpty(media.getId());
final int info;
switch (mediaType) {
case MEDIA_TYPE_IMAGE:
@ -153,7 +154,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
private void setPreview(final DirectItemVisualMedia visualMedia,
final MessageDirection messageDirection) {
final Media media = visualMedia.getMedia();
final boolean expired = media.getPk() == null;
final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) {
binding.preview.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.interfaces.FetchListener;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.webservices.GraphQLService;
@ -14,12 +14,12 @@ import awais.instagrabber.webservices.ServiceCallback;
public class LocationPostFetchService implements PostFetcher.PostFetchService {
private final LocationService locationService;
private final GraphQLService graphQLService;
private final LocationModel locationModel;
private final Location locationModel;
private String nextMaxId;
private boolean moreAvailable;
private final boolean isLoggedIn;
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) {
public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) {
this.locationModel = locationModel;
this.isLoggedIn = isLoggedIn;
locationService = isLoggedIn ? LocationService.getInstance() : null;
@ -47,8 +47,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
}
}
};
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb);
else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb);
if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb);
else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb);
}
@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;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
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) {
super(context, attrs, defStyleAttr);
this.waveBackgroundColor = context.getResources().getColor(R.color.white);
this.waveProgressColor = context.getResources().getColor(R.color.blue_800);
final TypedArray a = context.getTheme().obtainStyledAttributes(
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() {

View File

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

View File

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

View File

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

View File

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

View File

@ -3,14 +3,9 @@ package awais.instagrabber.fragments;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.SpannableStringBuilder;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
@ -45,7 +40,6 @@ import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.LocationFetcher;
import awais.instagrabber.asyncs.LocationPostFetchService;
import awais.instagrabber.asyncs.PostFetcher;
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.RepositoryCallback;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
//import awaisomereport.LogCollector;
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.Utils.logCollector;
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 opening = false;
private long locationId;
private LocationModel locationModel;
private Location locationModel;
private ActionMode actionMode;
private StoriesService storiesService;
private AsyncTask<?, ?, ?> currentlyExecuting;
private GraphQLService graphQLService;
private LocationService locationService;
private boolean isLoggedIn;
private boolean storiesFetching;
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
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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);
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
setHasOptionsMenu(true);
}
@ -354,8 +367,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private void init() {
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());
locationId = fragmentArgs.getLocationId();
locationDetailsBinding.favChip.setVisibility(View.GONE);
@ -377,42 +388,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
}
private void fetchLocationModel() {
stopCurrentExecutor();
binding.swipeRefreshLayout.setRefreshing(true);
currentlyExecuting = new LocationFetcher(locationId, result -> {
locationModel = result;
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);
if (isLoggedIn) locationService.fetch(locationId, cb);
else graphQLService.fetchLocation(locationId, cb);
}
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);
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
final String postCount = String.valueOf(locationModel.getPostCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
locationModel.getPostCount() > 2000000000L
? 2000000000
: locationModel.getPostCount().intValue(),
postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
locationDetailsBinding.mainLocPostCount.setText(span);
locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE);
locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location);
// final String postCount = String.valueOf(locationModel.getCount());
// final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
// locationModel.getPostCount() > 2000000000L
// ? 2000000000
// : locationModel.getPostCount().intValue(),
// postCount));
// span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
// span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
// locationDetailsBinding.mainLocPostCount.setText(span);
// locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE);
locationDetailsBinding.locationFullName.setText(locationModel.getName());
CharSequence biography = locationModel.getBio();
CharSequence biography = locationModel.getAddress() + "\n" + locationModel.getCity();
// binding.locationBiography.setCaptionIsExpandable(true);
// binding.locationBiography.setCaptionIsExpanded(true);
@ -423,22 +430,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} else {
locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE);
locationDetailsBinding.locationBiography.setText(biography);
locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle();
final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
});
locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText().trim();
navigateToProfile(originalText);
});
locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,
autoLinkItem.getOriginalText()
.trim()));
locationDetailsBinding.locationBiography
.addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));
// locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> {
// final NavController navController = NavHostFragment.findNavController(this);
// final Bundle bundle = new Bundle();
// final String originalText = autoLinkItem.getOriginalText().trim();
// bundle.putString(ARG_HASHTAG, originalText);
// navController.navigate(R.id.action_global_hashTagFragment, bundle);
// });
// locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> {
// final String originalText = autoLinkItem.getOriginalText().trim();
// navigateToProfile(originalText);
// });
// locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,
// autoLinkItem.getOriginalText()
// .trim()));
// locationDetailsBinding.locationBiography
// .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));
locationDetailsBinding.locationBiography.setOnLongClickListener(v -> {
Utils.copyText(context, biography);
return true;
@ -457,16 +464,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
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 FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
locationDetailsBinding.favChip.setVisibility(View.VISIBLE);
@ -481,7 +478,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId),
FavoriteType.LOCATION,
locationModel.getName(),
locationModel.getSdProfilePic(),
"res:/" + R.drawable.ic_location,
result.getDateAdded()
), new RepositoryCallback<Void>() {
@Override
@ -523,7 +520,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId),
FavoriteType.LOCATION,
locationModel.getName(),
locationModel.getSdProfilePic(),
"res:/" + R.drawable.ic_location,
new Date()
), new RepositoryCallback<Void>() {
@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() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null && locationModel != null) {

View File

@ -2,7 +2,6 @@ package awais.instagrabber.fragments;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
@ -37,9 +36,9 @@ import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs;
import awais.instagrabber.repositories.responses.NotificationImage;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.notification.NotificationImage;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
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) {
if (model == null) return;
final NavDirections action = StoryListViewerFragmentDirections
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(position));
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(model.getId()));
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
}

View File

@ -65,6 +65,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
@ -304,11 +306,18 @@ public class StoryViewerFragment extends Fragment {
// isNotification = fragmentArgs.getIsNotification();
final Type type = options.getType();
if (currentFeedStoryIndex >= 0) {
viewModel = type == Type.HIGHLIGHT
? type == Type.STORY_ARCHIVE
? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class)
: new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class)
: new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
switch (type) {
case HIGHLIGHT:
viewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class);
break;
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();
}
@ -728,9 +737,9 @@ public class StoryViewerFragment extends Fragment {
return;
}
final HighlightModel model = models.get(currentFeedStoryIndex);
currentStoryMediaId = model.getId();
currentStoryMediaId = parseStoryMediaId(model.getId());
currentStoryUsername = model.getTitle();
fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername);
fetchOptions = StoryViewerOptions.forStoryArchive(model.getId());
break;
}
}
@ -1139,4 +1148,20 @@ public class StoryViewerFragment extends Fragment {
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.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onPause() {
super.onPause();
unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
}
@Override
@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onDestroyView() {
super.onDestroyView();
unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
}
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests");
pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24)
.setVisible(false)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue());
inflater.inflate(R.menu.dm_inbox_menu, menu);
pendingRequestsMenuItem = menu.findItem(R.id.pending_requests);
pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached);
}
@Override
@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
@SuppressLint("UnsafeExperimentalUsageError")
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) {
final Context context = getContext();
if (context == null) return;

View File

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

View File

@ -1227,7 +1227,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
if (!isEmojiPickerShown) {
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) {
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
private User getUser(final long userId) {
for (final User user : users) {
@ -1426,58 +1340,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
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() {
if (heightProvider != null) return;
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.privatePage1.setImageResource(R.drawable.ic_outline_info_24);
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);
return;
}
@ -683,6 +679,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
binding.postsRecyclerView.refresh();
}
profileDetailsBinding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
profileDetailsBinding.isPrivate.setVisibility(profileModel.isPrivate() ? View.VISIBLE : View.GONE);
final long profileId = profileModel.getPk();
if (isLoggedIn) {
fetchStoryAndHighlights(profileId);
@ -917,11 +914,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} else {
profileDetailsBinding.mainFollowers.setClickable(false);
profileDetailsBinding.mainFollowing.setClickable(false);
// error
binding.privatePage1.setImageResource(R.drawable.lock);
binding.privatePage2.setText(R.string.priv_acc);
binding.privatePage.setVisibility(View.VISIBLE);
binding.postsRecyclerView.setVisibility(View.GONE);
binding.swipeRefreshLayout.setRefreshing(false);
}
}
@ -1206,7 +1203,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
private void updateSwipeRefreshState() {
Log.d("austin_debug", "usrs: " + 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.SwitchPreferenceCompat;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
@ -29,6 +31,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment {
}
screen.addPreference(getUpdateCheckPreference(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) {

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_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_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;
}
final DirectInbox inbox = inboxResource.data;
if (inbox == null) {
return null;
}
final List<DirectThread> threads = inbox.getThreads();
if (threads == null || threads.isEmpty()) {
return null;
@ -264,7 +267,10 @@ public final class ThreadManager {
}
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();
if (users != null) {
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 awais.instagrabber.repositories.responses.FeedFetchResponse;
import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;

View File

@ -16,4 +16,7 @@ public interface GraphQLRepository {
@GET("/explore/tags/{tag}/?__a=1")
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 awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.QueryMap;
public interface LocationRepository {
@GET("/api/v1/locations/{location}/info/")
Call<Place> fetch(@Path("location") final long locationId);
@GET("/api/v1/feed/location/{location}/")
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);
}
public static StoryViewerOptions forStoryArchive(final int position) {
return new StoryViewerOptions(position, Type.STORY_ARCHIVE);
public static StoryViewerOptions forStoryArchive(final String id) {
return new StoryViewerOptions(id, Type.STORY_ARCHIVE);
}
public long getId() {

View File

@ -5,22 +5,22 @@ import java.io.Serializable;
import awais.instagrabber.models.enums.FollowingType;
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 String id;
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,
final String name,
final String profilePicUrl,
final long mediaCount,
final FollowingType following) {
final FollowingType following,
final String searchResultSubtitle) {
this.id = id;
this.name = name;
this.profilePicUrl = profilePicUrl;
this.mediaCount = mediaCount;
this.following = following;
this.searchResultSubtitle = searchResultSubtitle;
}
public String getId() {
@ -31,10 +31,6 @@ public final class Hashtag implements Serializable {
return name;
}
public String getProfilePicUrl() {
return profilePicUrl;
}
public Long getMediaCount() {
return mediaCount;
}
@ -42,4 +38,8 @@ public final class Hashtag implements Serializable {
public FollowingType getFollowing() {
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 address;
private final String city;
private final float lng;
private final float lat;
private final double lng;
private final double lat;
public Location(final long pk,
final String shortName,
final String name,
final String address,
final String city,
final float lng,
final float lat) {
final double lng,
final double lat) {
this.pk = pk;
this.shortName = shortName;
this.name = name;
@ -48,22 +48,24 @@ public class Location implements Serializable {
return city;
}
public float getLng() {
public double getLng() {
return lng;
}
public float getLat() {
public double getLat() {
return lat;
}
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; }
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Location location = (Location) o;
return pk == location.pk &&
Float.compare(location.lng, lng) == 0 &&
Float.compare(location.lat, lat) == 0 &&
Double.compare(location.lng, lng) == 0 &&
Double.compare(location.lat, lat) == 0 &&
Objects.equals(shortName, location.shortName) &&
Objects.equals(name, location.name) &&
Objects.equals(address, location.address) &&

View File

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

View File

@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses;
import java.util.List;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
public class NewsInboxResponse {
private final NotificationCounts counts;
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);
}
public static class TextRange {
public static class TextRange implements Serializable {
private final int start;
private final int end;
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.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.util.List;
import java.util.Objects;
import awais.instagrabber.repositories.responses.Media;
public class EndOfFeedGroup implements Serializable {
private final String id;
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.util.List;

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses;
package awais.instagrabber.repositories.responses.notification;
public class NotificationImage {
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.activities.MainActivity;
import awais.instagrabber.repositories.responses.NotificationCounts;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ActivityCheckerService extends Service {
private static final String TAG = "ActivityCheckerService";
private static final int INITIAL_DELAY_MILLIS = 200;

View File

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

View File

@ -1,10 +1,8 @@
package awais.instagrabber.utils;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.util.Log;
import android.webkit.MimeTypeMap;
@ -13,8 +11,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.NetworkType;
@ -90,17 +86,17 @@ public final class DownloadUtils {
return dir;
}
// public static void dmDownload(@NonNull final Context context,
// @Nullable final String username,
// final String modelId,
// final String url) {
// if (url == null) return;
// if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
// dmDownloadImpl(context, username, modelId, url);
// } else if (context instanceof Activity) {
// ActivityCompat.requestPermissions((Activity) context, PERMS, 8020);
// }
// }
// public static void dmDownload(@NonNull final Context context,
// @Nullable final String username,
// final String modelId,
// final String url) {
// if (url == null) return;
// if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
// dmDownloadImpl(context, username, modelId, url);
// } else if (context instanceof Activity) {
// ActivityCompat.requestPermissions((Activity) context, PERMS, 8020);
// }
// }
private static void dmDownloadImpl(@NonNull final Context context,
@Nullable final String username,
@ -294,7 +290,8 @@ public final class DownloadUtils {
final int childPositionIfSingle) {
final Map<String, String> map = new HashMap<>();
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;
switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE:
@ -307,9 +304,8 @@ public final class DownloadUtils {
case MEDIA_TYPE_VOICE: {
final String url = getUrlOfType(media);
String fileName = media.getId();
final User user = media.getUser();
if (user != null) {
fileName = user.getUsername() + "_" + fileName;
if (mediaUser != null) {
fileName = mediaUser.getUsername() + "_" + fileName;
}
final File file = getDownloadSaveFile(downloadDir, fileName, url);
map.put(url, file.getAbsolutePath());

View File

@ -729,8 +729,11 @@ public final class ResponseBodyUtils {
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
final JSONArray displayResources = feedItem.getJSONArray("display_resources");
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++) {
final JSONObject displayResource = displayResources.getJSONObject(i);
candidates.add(new MediaCandidate(
@ -739,6 +742,7 @@ public final class ResponseBodyUtils {
displayResource.getString("src")
));
}
}
final ImageVersions2 imageVersions2 = new ImageVersions2(candidates);
User user = backup;
@ -943,8 +947,7 @@ public final class ResponseBodyUtils {
// }
public static StoryModel parseStoryItem(final JSONObject data,
final boolean isLoc,
final boolean isHashtag,
final boolean isLocOrHashtag,
final String username) throws JSONException {
final boolean isVideo = data.has("video_duration");
final StoryModel model = new StoryModel(data.getString("id"),
@ -952,9 +955,7 @@ public final class ResponseBodyUtils {
.getString("url"), null,
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0),
(isLoc || isHashtag)
? data.getJSONObject("user").getString("username")
: username,
isLocOrHashtag ? data.getJSONObject("user").getString("username") : username,
data.getJSONObject("user").getLong("pk"),
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_UNIT;
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_THEME;
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();
}
public boolean hasPreference(final String key) {
return sharedPreferences != null && sharedPreferences.contains(key);
}
@StringDef(
{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,
@ -156,7 +161,7 @@ public final class SettingsHelper {
@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,
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 {}
@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();
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) {

View File

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

View File

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

View File

@ -12,10 +12,10 @@ import java.util.Map;
import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository;
import awais.instagrabber.repositories.responses.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.FeedFetchResponse;
import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
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.GraphQLUserListFetchResponse;
import awais.instagrabber.repositories.responses.Hashtag;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.User;
@ -392,9 +393,53 @@ public class GraphQLService extends BaseService {
callback.onSuccess(new Hashtag(
body.getString(Constants.EXTRAS_ID),
body.getString("name"),
body.getString("profile_pic_url"),
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) {
Log.e(TAG, "onResponse", e);
if (callback != null) {

View File

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

View File

@ -16,6 +16,10 @@ import awais.instagrabber.repositories.responses.NewsInboxResponse;
import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs;
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.UserSearchResponse;
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 {
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) {
callback.onFailure(e);
}
@ -185,7 +185,7 @@ public class StoriesService extends BaseService {
final boolean isBestie = node.optBoolean("has_besties_media", false);
StoryModel firstStoryModel = 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));
} 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 String url = buildUrl(options);
final Call<String> userStoryCall = repository.getUserStory(url);
final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION;
final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG;
final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT;
final boolean isLocOrHashtag = options.getType() == StoryViewerOptions.Type.LOCATION || options.getType() == StoryViewerOptions.Type.HASHTAG;
final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT || options.getType() == StoryViewerOptions.Type.STORY_ARCHIVE;
userStoryCall.enqueue(new Callback<String>() {
@Override
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);
if (!isHighlight) {
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
data = data.optJSONObject((isLocOrHashtag) ? "story" : "reel");
} else {
data = data.getJSONObject("reels").optJSONObject(options.getName());
}
@ -385,8 +384,7 @@ public class StoriesService extends BaseService {
String username = null;
if (data != null
// && localUsername == null
&& !isLoc
&& !isHashtag) {
&& !isLocOrHashtag) {
username = data.getJSONObject("user").getString("username");
}
@ -394,12 +392,11 @@ public class StoriesService extends BaseService {
if (data != null
&& (media = data.optJSONArray("items")) != null
&& media.length() > 0 && media.optJSONObject(0) != null) {
final int mediaLen = media.length();
final List<StoryModel> models = new ArrayList<>();
for (int i = 0; i < mediaLen; ++i) {
data = media.getJSONObject(i);
models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, username));
models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username));
}
callback.onSuccess(models);
} else {
@ -540,6 +537,7 @@ public class StoriesService extends BaseService {
id = String.valueOf(options.getId());
break;
case HIGHLIGHT:
case STORY_ARCHIVE:
builder.append("feed/reels_media/?user_ids=");
id = options.getName();
break;
@ -547,15 +545,12 @@ public class StoriesService extends BaseService {
break;
// case FEED_STORY_POSITION:
// break;
// case STORY_ARCHIVE:
// break;
}
if (id == null) {
return null;
}
final String userId = id.replace(":", "%3A");
builder.append(userId);
if (type != StoryViewerOptions.Type.HIGHLIGHT) {
builder.append(id);
if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) {
builder.append("/story/");
}
return builder.toString();

View File

@ -1,50 +1,35 @@
package awaisomereport;
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.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 {
private static final String TAG = CrashReporter.class.getSimpleName();
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 Thread.UncaughtExceptionHandler defaultEH;
public static CrashReporter get(final Application application) {
if (reporterInstance == null) reporterInstance = new CrashReporter(application);
if (reporterInstance == null) {
reporterInstance = new CrashReporter(application);
}
return reporterInstance;
}
private CrashReporter(@NonNull final Application application) {
this.application = application;
this.email = "barinsta@austinhuang.me";
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
crashHandler = new CrashHandler(application);
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
}
public void start() {
if (!startAttempted) {
defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
startAttempted = true;
}
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(@NonNull final Thread t, @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.");
reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.");
reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString());
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);
if (crashHandler == null) {
defaultEH.uncaughtException(t, exception);
return;
}
crashHandler.uncaughtException(t, exception, defaultEH);
}
}

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
public void onClick(@NonNull final View v) {
if (v == btnReport)
CrashReporter.get(getApplication()).startCrashEmailIntent(this);
if (v == btnReport) {
CrashReporterHelper.startCrashEmailIntent(this);
}
finish();
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:paddingTop="12dp"
android:paddingBottom="12dp"
android:textColor="@color/white"
android:textColor="?dmInputTextColor"
android:textColorHint="@color/grey_500"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,25 +15,25 @@
android:layout_height="@dimen/profile_picture_size"
android:background="?selectableItemBackgroundBorderless"
app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toStartOf="@id/mainLocPostCount"
app:layout_constraintEnd_toStartOf="@id/btnMap"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainLocPostCount"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toTopOf="@id/btnMap"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="35 Posts" />
<!-- <androidx.appcompat.widget.AppCompatTextView-->
<!-- android:id="@+id/mainLocPostCount"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="0dp"-->
<!-- android:gravity="center_vertical"-->
<!-- android:maxLines="1"-->
<!-- android:paddingStart="12dp"-->
<!-- android:paddingEnd="12dp"-->
<!-- android:textAppearance="@style/TextAppearance.AppCompat"-->
<!-- app:layout_constraintBottom_toTopOf="@id/btnMap"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toEndOf="@id/mainLocationImage"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- tools:text="35 Posts" />-->
<com.google.android.material.chip.Chip
android:id="@+id/btnMap"
@ -44,9 +44,8 @@
app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_map_24"
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_constraintTop_toBottomOf="@id/mainLocPostCount"
app:rippleColor="@color/grey_500"
tools:visibility="visible" />
@ -60,8 +59,8 @@
app:chipIcon="@drawable/ic_outline_star_plus_24"
app:chipIconTint="@color/yellow_800"
app:layout_constraintBottom_toBottomOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/btnMap"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/btnMap"
app:rippleColor="@color/yellow_400" />
<androidx.appcompat.widget.AppCompatTextView
@ -90,27 +89,10 @@
android:padding="8dp"
android:background="?android:selectableItemBackground"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toTopOf="@id/locationUrl"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationFullName"
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>

View File

@ -36,6 +36,7 @@
app:chipIconTint="@color/deep_purple_200"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/purple_200"
tools:visibility="visible" />
@ -50,6 +51,7 @@
app:chipBackgroundColor="@null"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/btnFollow"
app:layout_constraintTop_toTopOf="parent"
tools:text="omg what do u expect"
tools:visibility="visible" />
@ -66,6 +68,7 @@
app:chipIconTint="@color/blue_700"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/mainStatus"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/blue_A400"
tools:visibility="visible" />
@ -81,6 +84,7 @@
app:chipIconTint="@color/red_600"
app:layout_constraintBottom_toTopOf="@id/fav_chip"
app:layout_constraintStart_toEndOf="@id/btnSaved"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/red_300"
tools:visibility="visible" />
@ -109,6 +113,7 @@
app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_person_pin_24"
app:chipIconTint="@color/deep_orange_800"
app:layout_constraintBottom_toTopOf="@+id/mainFullName"
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
app:layout_constraintTop_toBottomOf="@id/fav_chip"
app:rippleColor="@color/deep_orange_400"
@ -124,6 +129,7 @@
app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_round_send_24"
app:chipIconTint="@color/green"
app:layout_constraintBottom_toTopOf="@+id/mainFullName"
app:layout_constraintStart_toEndOf="@id/btnTagged"
app:layout_constraintTop_toBottomOf="@id/fav_chip"
app:rippleColor="@color/green"
@ -161,6 +167,23 @@
app:srcCompat="@drawable/verified"
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
android:id="@+id/fav_cb"
android:layout_width="wrap_content"

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