Browse Source

Merge branch 'master' into retrofit-intercept-errors

renovate/org.robolectric-robolectric-4.x
Austin Huang 4 years ago
committed by GitHub
parent
commit
6965fbdc9a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      .all-contributorsrc
  2. 67
      .github/workflows/github_nightly_release.yml
  3. 68
      .github/workflows/github_pre_release.yml
  4. 18
      .github/workflows/label-bugs.yml
  5. 18
      .github/workflows/label-duplicates.yml
  6. 3
      .gitignore
  7. 2
      .idea/.name
  8. 22
      README.md
  9. 54
      app/build.gradle
  10. 13
      app/sentry.gradle
  11. 40
      app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java
  12. 56
      app/src/fdroid/java/awaisomereport/CrashHandler.java
  13. 10
      app/src/github/AndroidManifest.xml
  14. 83
      app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java
  15. 59
      app/src/github/java/awaisomereport/CrashHandler.java
  16. 6
      app/src/github/res/values/strings.xml
  17. 37
      app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
  18. 120
      app/src/main/java/awais/instagrabber/activities/MainActivity.java
  19. 2
      app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
  20. 26
      app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java
  21. 4
      app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
  22. 9
      app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java
  23. 7
      app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java
  24. 81
      app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java
  25. 10
      app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
  26. 113
      app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java
  27. 18
      app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java
  28. 69
      app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java
  29. 2
      app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java
  30. 2
      app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
  31. 8
      app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
  32. 149
      app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
  33. 7
      app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
  34. 2
      app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
  35. 39
      app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
  36. 31
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
  37. 15
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
  38. 146
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
  39. 8
      app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
  40. 10
      app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java
  41. 14
      app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java
  42. 1
      app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java
  43. 6
      app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java
  44. 8
      app/src/main/java/awais/instagrabber/managers/ThreadManager.java
  45. 56
      app/src/main/java/awais/instagrabber/models/LocationModel.java
  46. 51
      app/src/main/java/awais/instagrabber/models/SuggestionModel.java
  47. 2
      app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
  48. 3
      app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java
  49. 3
      app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
  50. 15
      app/src/main/java/awais/instagrabber/repositories/SearchRepository.java
  51. 4
      app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java
  52. 18
      app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java
  53. 18
      app/src/main/java/awais/instagrabber/repositories/responses/Location.java
  54. 1
      app/src/main/java/awais/instagrabber/repositories/responses/Media.java
  55. 3
      app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java
  56. 43
      app/src/main/java/awais/instagrabber/repositories/responses/Place.java
  57. 2
      app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java
  58. 2
      app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java
  59. 4
      app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java
  60. 2
      app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java
  61. 4
      app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java
  62. 2
      app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java
  63. 4
      app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java
  64. 2
      app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java
  65. 2
      app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java
  66. 38
      app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java
  67. 48
      app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java
  68. 4
      app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java
  69. 2
      app/src/main/java/awais/instagrabber/utils/Constants.java
  70. 34
      app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
  71. 27
      app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
  72. 7
      app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
  73. 2
      app/src/main/java/awais/instagrabber/utils/TextUtils.java
  74. 24
      app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java
  75. 1
      app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
  76. 2
      app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java
  77. 8
      app/src/main/java/awais/instagrabber/webservices/FeedService.java
  78. 49
      app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
  79. 50
      app/src/main/java/awais/instagrabber/webservices/LocationService.java
  80. 4
      app/src/main/java/awais/instagrabber/webservices/NewsService.java
  81. 50
      app/src/main/java/awais/instagrabber/webservices/SearchService.java
  82. 25
      app/src/main/java/awais/instagrabber/webservices/StoriesService.java
  83. 179
      app/src/main/java/awaisomereport/CrashReporter.java
  84. 134
      app/src/main/java/awaisomereport/CrashReporterHelper.java
  85. 5
      app/src/main/java/awaisomereport/ErrorReporterActivity.java
  86. 7
      app/src/main/java/awaisomereport/ICrashHandler.java
  87. BIN
      app/src/main/res/drawable/ic_hashtag.png
  88. 2
      app/src/main/res/layout/fragment_direct_messages_thread.xml
  89. 4
      app/src/main/res/layout/fragment_profile.xml
  90. 180
      app/src/main/res/layout/fragment_story_viewer.xml
  91. 1
      app/src/main/res/layout/layout_dm_base.xml
  92. 4
      app/src/main/res/layout/layout_dm_link.xml
  93. 2
      app/src/main/res/layout/layout_dm_media_share.xml
  94. 6
      app/src/main/res/layout/layout_dm_profile.xml
  95. 1
      app/src/main/res/layout/layout_dm_reel_share.xml
  96. 1
      app/src/main/res/layout/layout_dm_story_share.xml
  97. 1
      app/src/main/res/layout/layout_dm_text.xml
  98. 4
      app/src/main/res/layout/layout_dm_voice_media.xml
  99. 56
      app/src/main/res/layout/layout_location_details.xml
  100. 23
      app/src/main/res/layout/layout_profile_details.xml

45
.all-contributorsrc

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

67
.github/workflows/github_nightly_release.yml

@ -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}}"

68
.github/workflows/github_pre_release.yml

@ -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

@ -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/[email protected]
with:
actions: 'add-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'bug'

18
.github/workflows/label-duplicates.yml

@ -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/[email protected]
with:
actions: 'add-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'duplicate'

3
.gitignore

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

2
.idea/.name

@ -1 +1 @@
InstaGrabber
Barinsta

22
README.md

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

54
app/build.gradle

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

13
app/sentry.gradle

@ -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)
}

40
app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java

@ -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();
}
}

56
app/src/fdroid/java/awaisomereport/CrashHandler.java

@ -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;
// }
}

10
app/src/github/AndroidManifest.xml

@ -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>

83
app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java

@ -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;
});
}
}

59
app/src/github/java/awaisomereport/CrashHandler.java

@ -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);
}
}

6
app/src/github/res/values/strings.xml

@ -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>

37
app/src/main/java/awais/instagrabber/InstaGrabberApplication.java

@ -35,16 +35,16 @@ public final class InstaGrabberApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// final Set<RequestListener> requestListeners = new HashSet<>();
// 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) { if (BuildConfig.DEBUG) {
try { 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) { if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper()); applicationHandler = new Handler(getApplicationContext().getMainLooper());

120
app/src/main/java/awais/instagrabber/activities/MainActivity.java

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

2
app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java

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

26
app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java

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

4
app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java

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

9
app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java

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

7
app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java

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

81
app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java

@ -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);
}
}

10
app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java

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

113
app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java

@ -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);
}
}

18
app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java

@ -1,6 +1,7 @@
package awais.instagrabber.customviews.masoudss_waveform; package awais.instagrabber.customviews.masoudss_waveform;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapShader; import android.graphics.BitmapShader;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -50,8 +51,21 @@ public final class WaveformSeekBar extends View {
public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
this.waveBackgroundColor = context.getResources().getColor(R.color.white);
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() { private float getSampleMax() {

69
app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java

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

2
app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java

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

2
app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java

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

8
app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java

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

149
app/src/main/java/awais/instagrabber/fragments/LocationFragment.java

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

7
app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java

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

2
app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java

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

39
app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java

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

31
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
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 @Override
@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
@SuppressLint("UnsafeExperimentalUsageError") @SuppressLint("UnsafeExperimentalUsageError")
private void attachPendingRequestsBadge(@Nullable final Integer count) { private void attachPendingRequestsBadge(@Nullable final Integer count) {
if (pendingRequestsMenuItem == null) return;
if (pendingRequestsMenuItem == null) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
attachPendingRequestsBadge(count);
}
}, 500);
return;
}
if (pendingRequestTotalBadgeDrawable == null) { if (pendingRequestTotalBadgeDrawable == null) {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;

15
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java

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

146
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java

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

8
app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java

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

10
app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java

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

14
app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java

@ -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);
}

1
app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java

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

6
app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java

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

8
app/src/main/java/awais/instagrabber/managers/ThreadManager.java

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

56
app/src/main/java/awais/instagrabber/models/LocationModel.java

@ -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; }
}

51
app/src/main/java/awais/instagrabber/models/SuggestionModel.java

@ -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());
}
}

2
app/src/main/java/awais/instagrabber/repositories/FeedRepository.java

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

3
app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java

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

3
app/src/main/java/awais/instagrabber/repositories/LocationRepository.java

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

15
app/src/main/java/awais/instagrabber/repositories/SearchRepository.java

@ -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);
}

4
app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java

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

18
app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java

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

18
app/src/main/java/awais/instagrabber/repositories/responses/Location.java

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

1
app/src/main/java/awais/instagrabber/repositories/responses/Media.java

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

3
app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java

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

43
app/src/main/java/awais/instagrabber/repositories/responses/Place.java

@ -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;
}
}

2
app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java

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

2
app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java → app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java

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

4
app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java → app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java

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

2
app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java → app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java

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

4
app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java → app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java

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

2
app/src/main/java/awais/instagrabber/repositories/responses/Notification.java → app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java

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

4
app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java → app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java

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

2
app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java → app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java

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

2
app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java → app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java

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

38
app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java

@ -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;
}
}

48
app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java

@ -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;
}
}

4
app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java

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

2
app/src/main/java/awais/instagrabber/utils/Constants.java

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

34
app/src/main/java/awais/instagrabber/utils/DownloadUtils.java

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

27
app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java

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

7
app/src/main/java/awais/instagrabber/utils/SettingsHelper.java

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

2
app/src/main/java/awais/instagrabber/utils/TextUtils.java

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

24
app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java

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

1
app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java

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

2
app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java

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

8
app/src/main/java/awais/instagrabber/webservices/FeedService.java

@ -12,10 +12,10 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository; import awais.instagrabber.repositories.FeedRepository;
import awais.instagrabber.repositories.responses.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.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.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;

49
app/src/main/java/awais/instagrabber/webservices/GraphQLService.java

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

50
app/src/main/java/awais/instagrabber/webservices/LocationService.java

@ -5,7 +5,9 @@ import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.LocationRepository; import awais.instagrabber.repositories.LocationRepository;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.LocationFeedResponse; import awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
@ -67,34 +69,24 @@ public class LocationService extends BaseService {
}); });
} }
// private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException {
// 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);
}
}
});
}
} }

4
app/src/main/java/awais/instagrabber/webservices/NewsService.java

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

50
app/src/main/java/awais/instagrabber/webservices/SearchService.java

@ -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());
}
}

25
app/src/main/java/awais/instagrabber/webservices/StoriesService.java

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

179
app/src/main/java/awaisomereport/CrashReporter.java

@ -1,50 +1,35 @@
package awaisomereport; package awaisomereport;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.utils.Utils;
public final class CrashReporter implements Thread.UncaughtExceptionHandler { public final class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final String TAG = CrashReporter.class.getSimpleName();
private static CrashReporter reporterInstance; private static CrashReporter reporterInstance;
private final Application application;
private final String email;
// private final File crashLogsZip;
// private final File crashLogsZip;
private final CrashHandler crashHandler;
private boolean startAttempted = false; private boolean startAttempted = false;
private Thread.UncaughtExceptionHandler defaultEH;
public static CrashReporter get(final Application application) { public static CrashReporter get(final Application application) {
if (reporterInstance == null) reporterInstance = new CrashReporter(application);
if (reporterInstance == null) {
reporterInstance = new CrashReporter(application);
}
return reporterInstance; return reporterInstance;
} }
private CrashReporter(@NonNull final Application application) { private CrashReporter(@NonNull final Application application) {
this.application = application;
this.email = "[email protected]";
// 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() { public void start() {
if (!startAttempted) { if (!startAttempted) {
defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this); Thread.setDefaultUncaughtExceptionHandler(this);
startAttempted = true; startAttempted = true;
} }
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
@Override @Override
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) {
final StringBuilder reportBuilder = new StringBuilder();
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);
} }
} }

134
app/src/main/java/awaisomereport/CrashReporterHelper.java

@ -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);
}
}
}

5
app/src/main/java/awaisomereport/ErrorReporterActivity.java

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

7
app/src/main/java/awaisomereport/ICrashHandler.java

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

BIN
app/src/main/res/drawable/ic_hashtag.png

After

Width: 132  |  Height: 132  |  Size: 5.5 KiB

2
app/src/main/res/layout/fragment_direct_messages_thread.xml

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

4
app/src/main/res/layout/fragment_profile.xml

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

180
app/src/main/res/layout/fragment_story_viewer.xml

@ -9,8 +9,7 @@
android:id="@+id/story_container" android:id="@+id/story_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintBottom_toTopOf="@id/storiesList"
app:layout_constraintBottom_toTopOf="@id/postActions"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -37,93 +36,98 @@
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone" /> android:visibility="gone" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/postActions"
</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
android:id="@+id/viewStoryPost"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#0000">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/viewStoryPost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/view_story_post"
android:textColor="@color/btn_green_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/poll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/vote_story_poll"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/answer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/respond_story"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/quiz"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_quiz"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_slider"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/swipeUp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SAMPLE TEXT"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mention"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_mentions"
android:textColor="@color/btn_orange_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/spotify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/spotify"
android:textColor="@color/btn_green_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_green_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
</FrameLayout>
android:layout_weight="1"
android:text="@string/view_story_post"
android:textColor="@color/btn_green_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/poll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/vote_story_poll"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/answer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/respond_story"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/quiz"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_quiz"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_slider"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/swipeUp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SAMPLE TEXT"
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mention"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/story_mentions"
android:textColor="@color/btn_orange_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/spotify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/spotify"
android:textColor="@color/btn_green_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_green_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnBackward" android:id="@+id/btnBackward"
@ -144,7 +148,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
app:layout_constraintTop_toBottomOf="@id/story_container"
app:layout_constraintTop_toBottomOf="@id/postActions"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnForward" app:layout_constraintEnd_toStartOf="@id/btnForward"
app:layout_constraintStart_toEndOf="@id/btnBackward" /> app:layout_constraintStart_toEndOf="@id/btnBackward" />

1
app/src/main/res/layout/layout_dm_base.xml

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

4
app/src/main/res/layout/layout_dm_link.xml

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

2
app/src/main/res/layout/layout_dm_media_share.xml

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

6
app/src/main/res/layout/layout_dm_profile.xml

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

1
app/src/main/res/layout/layout_dm_reel_share.xml

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

1
app/src/main/res/layout/layout_dm_story_share.xml

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

1
app/src/main/res/layout/layout_dm_text.xml

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

4
app/src/main/res/layout/layout_dm_voice_media.xml

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

56
app/src/main/res/layout/layout_location_details.xml

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

23
app/src/main/res/layout/layout_profile_details.xml

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

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

Loading…
Cancel
Save