Merge remote-tracking branch 'origin/task/update-feed-view' into task/update-feed-view

This commit is contained in:
Ammar Githam 2020-08-30 23:29:31 +09:00
commit ded09ab237
26 changed files with 425 additions and 107 deletions

148
.all-contributorsrc Normal file
View File

@ -0,0 +1,148 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "austinhuang0131",
"name": "Austin Huang",
"avatar_url": "https://avatars1.githubusercontent.com/u/16656689",
"profile": "https://austinhuang.me",
"contributions": [
"code",
"doc",
"question",
"translation",
"ideas"
]
},
{
"login": "ammargitham",
"name": "Ammar Githam",
"avatar_url": "https://avatars0.githubusercontent.com/u/8017365",
"profile": "https://github.com/ammargitham",
"contributions": [
"code",
"design",
"ideas",
"maintenance",
"question"
]
},
{
"login": "AwaisKing",
"name": "AWAiS",
"avatar_url": "https://avatars3.githubusercontent.com/u/5278488",
"profile": "http://rerolledgeek.blogspot.com/",
"contributions": [
"code",
"ideas"
]
},
{
"login": "Shadowspear123",
"name": "Shadowspear123",
"avatar_url": "https://avatars1.githubusercontent.com/u/50462281",
"profile": "https://github.com/Shadowspear123",
"contributions": [
"blog",
"bug",
"ideas",
"question",
"userTesting"
]
},
{
"login": "KevinNThomas",
"name": "Kevin Thomas",
"avatar_url": "https://avatars2.githubusercontent.com/u/15370181",
"profile": "http://kevinthomas.dev",
"contributions": [
"financial"
]
},
{
"login": "e-edgren",
"name": "Airikr",
"avatar_url": "https://avatars0.githubusercontent.com/u/53869451",
"profile": "https://airikr.me/",
"contributions": [
"question",
"ideas"
]
},
{
"login": "Galang23",
"name": "Galang23",
"avatar_url": "https://avatars3.githubusercontent.com/u/13700948",
"profile": "https://github.com/Galang23",
"contributions": [
"question",
"translation"
]
},
{
"login": "kernoeb",
"name": "kernoeb",
"avatar_url": "https://avatars3.githubusercontent.com/u/24623168",
"profile": "https://becauseofprog.fr/",
"contributions": [
"translation"
]
},
{
"login": "Lego8486",
"name": "Ten_Lego",
"avatar_url": "https://avatars1.githubusercontent.com/u/47414485",
"profile": "https://github.com/Lego8486",
"contributions": [
"translation"
]
},
{
"login": "MoaufmKlo",
"name": "MoaufmKlo",
"avatar_url": "https://avatars1.githubusercontent.com/u/45636897",
"profile": "https://github.com/MoaufmKlo",
"contributions": [
"translation"
]
},
{
"login": "peterge1998",
"name": "peterge1998",
"avatar_url": "https://avatars2.githubusercontent.com/u/47355238",
"profile": "https://github.com/peterge1998",
"contributions": [
"translation"
]
},
{
"login": "RAMAR-RAR",
"name": "RAMAR-RAR",
"avatar_url": "https://avatars3.githubusercontent.com/u/47423745",
"profile": "https://github.com/RAMAR-RAR",
"contributions": [
"translation"
]
},
{
"login": "wagnim",
"name": "wagnim",
"avatar_url": "https://avatars0.githubusercontent.com/u/30241419",
"profile": "https://github.com/wagnim",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 6,
"projectName": "instagrabber",
"projectOwner": "austinhuang0131",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true,
"commitConvention": "none"
}

View File

@ -1,3 +0,0 @@
component_depth: 7
languages:
- java

73
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,73 @@
## WARNING
* All forks must respect [GPLv3](https://www.gnu.org/licenses/gpl-3.0.html). Please report violations in Issues or [confidentially](https://austinhuang.me/#hey-you-look-cool).
* Although publishing a fork is allowed by license, it is strongly discouraged to do so as it divides the effort and creates confusion. It is, therefore, recommended to send a pull request back to us, so that the larger community can enjoy your improvement. (This does not apply if you're adapting this app for a different platform other than Instagram.)
## Contributing
Thank you for your interest in InstaGrabber!
Our vision of InstaGrabber is an open source true alternative of the official Instagram app. It is Austin's pursuit of a libre life that lead him to this app during its abandonment, and it was one unresolved bug that made him have the enthusiasm in implementing numerous features for this app, despite having 0 knowledge of Java beforehand.
As we grow in popularity, it becomes apparent that we are short on hands. Every contribution counts!
## I want to help coding it!
Great!
Generally, we want to imitate features in the actual Instagram app. There are many Instagram private API repositories on GitHub for you to refer to. Note that you should minimize POST: If a job should be done with GET, then there has to be a GET endpoint. (Indeed, sometimes you need multiple repositories for reference.)
As long as you have tested your version (please indicate device and API version) and make sure it works, then you can submit a PR! Large UI changes have to be voted on by the community, so it would be helpful to upload some screenshots.
Check errors are for reference only. Try to minimize them, but usually they don't make a big difference.
**NEVER touch the l10n-master branch.** It's automatically managed by Crowdin.
### Structure
It is preferred that you read the scripts yourself, as my understanding (and presentation) may be basic.
* `awais.instagrabber`
* `activities`: Scripts directly binding to each view. Assigns `adapters`.
* `adapters`: Scripts used to present a list of `models` from `asyncs` into `activities`.
* Those inside `viewholder` are for frontend, while others are for backend.
* `asyncs`: Scripts used to communicate with Instagram. Returns `models` which is sent to `adapters`.
* `asyncs.i`: Scripts that fetch data from `i.instagram.com`, except DM.
* `asyncs.direct_messages`: As the name suggests, communication scripts used for DM.
* `customviews`: Custom frontend components for this app.
* `dialogs`: Scripts directly binding to dialogs (i.e. those that are nested in the main view).
* `fragments.directmessages`: Scripts directly binding to each fragment ("small views") within `DirectMessageActivity` in `activities`.
* `interfaces`: Custom backend components for this app.
* `models`: Data structure for Instagram API responses from `asyncs`.
* `utils`: Various tools.
* `MainHelper.java` is basically an extension of `activities.main`.
* `awaisomereport`: Crash reporter. Shouldn not require too much maintenance.
* `thoughtbot.expandableadapter`: These are for the follower comparison view, which allows grouping users.
### I can't code Java, but I want to!
Fun fact: Austin took over this project and learned Java on the fly (I'm not joking, I only do JavaScript before taking this over).
Even though Java is quite annoying, it is still possible to learn it by trying to understand what these code do (Easier if you have coding experience in other languages).
If you have questions, don't be afraid to ask for help from any current maintainer!
## I found a bug!
**Please read [FAQ](https://instagrabber.austinhuang.me/faq) first.**
Bugs are inevitable during active development, as nobody can cover all the possible test cases.
You can either email your crash dump to `instagrabber@austinhuang.me` (The crash reporter will fill in this address for you) or create a GitHub issue. If you're on GitHub, please follow the template. If you're reporting by email, your email address will be published in the GitHub issue. You can contact me [privately](https://austinhuang.me/#hey-you-look-cool) or [through support channels](https://instagrabber.austinhuang.me/#what-can-i-do) to remove it.
Generally, reporting bugs directly in support channels is not recommended, as they can be difficult to find.
### I want to help... in some other way.
You can...
* translate it [![badge](https://badges.crowdin.net/instagrabber/localized.svg)](https://crowdin.com/project/instagrabber)
* promote it (reddit [r/AndroidApps](https://www.reddit.com/r/androidapps/comments/i30tpp/instagrabber_an_open_source_instagram_client/), YouTube [HowToMen](https://www.youtube.com/watch?v=1Um2ZJG_mB4), [Ekşi Sözlük](https://eksisozluk.com/instagrabber--6643143))
* star it [![stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://github.com/austinhuang0131/instagrabber/stargazers)
Happy contributing!

View File

@ -4,31 +4,27 @@ about: App crashing? You seeing a confusing error message? Report them here!
title: "[BUG]"
labels: bug
assignees: ''
---
<!-- FOLLOW THIS FORMAT -->
- [ ] My app is on the latest version. I understand that any other version is not supported.
- [ ] I have read [the FAQ](https://instagrabber.austinhuang.me/faq).
<!--
First, describe the bug here. Don't stress too much, but do include the key points.
Describe the bug here. Don't stress too much, but do include the key points.
-->
## Steps
<!--
DETAILED Steps to reproduce the behaviour. If you do not describe this part clearly, I may ignore the issue.
DETAILED Steps to reproduce the behaviour.
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
### Expected Behaviour
## Screenshots
<!-- if applicable, remove otherwise -->
## Environment
- Device:

View File

@ -8,9 +8,9 @@ assignees: ''
---
<!--
The only source of Java experience for the owner is from this app
Large features will either take a lot of time or another maintainer
Just remember to be patient, and if you have skills, try DIY
Also write title after [FTR]
-->
**Is your feature request related to a problem? Please describe.**
@ -19,8 +19,5 @@ A clear and concise description of what the problem is. Ex. I'm always frustrate
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -8,37 +8,20 @@
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/instagrabber/stargazers/)
*As featured on [HowToMen](https://youtu.be/1Um2ZJG_mB4) and [GitHub Trending](https://github.com/ifyour/github-trending-archive/blob/master/archives/2020-08-05.md)*
For documentation, visit [InstaGrabber.AustinHuang.me](https://instagrabber.austinhuang.me).
### Download
<a href="https://f-droid.org/en/packages/me.austinhuang.instagrabber/"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="100"></a>
**By installing, you indicate your acceptance of [Disclosure and Privacy Policy](https://instagrabber.austinhuang.me/disclosure).**
Or you can download from [GitHub releases](https://github.com/austinhuang0131/instagrabber/releases/latest). Expand "Assets" and download the APK file.
Download from [GitHub releases](https://github.com/austinhuang0131/instagrabber/releases/latest) (recommended). Expand "Assets" and download the APK file. Or...
<a href="https://f-droid.org/en/packages/me.austinhuang.instagrabber/"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75"></a>
(...[but they're slow.](https://instagrabber.austinhuang.me/faq#f-droid))
Version status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.instagrabber.svg) vs. ![GitHub](https://img.shields.io/github/release/austinhuang0131/instagrabber.svg?logo=github)
### Presentation
InstaGrabber is an alternative Instagram client for Android. You can...
* **Search**: Profile / Hashtag / Location
* **Profile**: View posts (timeline & face-tagged), copy bio, view stories\* (Highlight & status), follow\*, restrict\*, block\*, compare following/followers\*<sup>,3</sup>
* **Hashtag**: View posts (newest only), view stories\*, follow\*
* **Location**: View posts (newest only), view stories\*, open map
* **Post**: View, download (+ batch download selected posts), copy captions, like/save\*
* **Story**: View (with [storiesig](https://storiesig.com) support<sup>1</sup> and incognito mode<sup>2</sup>), download, interact with stickers\*, reply as DM\*
* **Comment**: View (+ copy), write\*, reply\*, like\*, delete\*
* **DM\***: View, download attachments, text reply
* Viewing\* your own feed, discover, saved, and liked posts
* Adding personal bookmarks to accounts/hashtags/locations locally
We support private accounts!\*
It can be used as a drop-in replacement for read (and some write) functionalities of the official Instagram app, with unnecessary components stripped.
<sub>* Requires [login](#how-to-log-in). You must be a current follower of the desired private accounts, this app cannot hack people (which I have to state despite the obvious)!</sub>
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/images/en-US/phoneScreenshots/1.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg" alt="Profile" width="15%"/></a>
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/images/en-US/phoneScreenshots/2.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" alt="Post" width="15%"/></a>
<a href="https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/images/en-US/phoneScreenshots/3.jpg"><img src="./fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg" alt="Comments" width="15%"/></a>
@ -54,6 +37,43 @@ It can be used as a drop-in replacement for read (and some write) functionalitie
* Telegram: [@Grabber_App](https://t.me/grabber_app)
* Discord: [https://discord.gg/YtEDzN2](https://discord.gg/YtEDzN2)
### Contributors
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Prominent contributors are listed here in the [all-contributors](https://allcontributors.org/) specifications. See [emoji key](https://allcontributors.org/docs/en/emoji-key).
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<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/instagrabber/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/instagrabber/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="#translation-austinhuang0131" 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/instagrabber/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="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/instagrabber/commits?author=AwaisKing" title="Code">💻</a> <a href="#ideas-AwaisKing" title="Ideas, Planning, & Feedback">🤔</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/instagrabber/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> <a href="#userTesting-Shadowspear123" title="User Testing">📓</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://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="#question-e-edgren" title="Answering Questions">💬</a> <a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<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="#question-Galang23" title="Answering Questions">💬</a> <a href="#translation-Galang23" 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="#translation-kernoeb" 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="#translation-Lego8486" 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="#translation-MoaufmKlo" 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="#translation-peterge1998" 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="#translation-RAMAR-RAR" title="Translation">🌍</a></td>
</tr>
<tr>
<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="#translation-wagnim" title="Translation">🌍</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
### License
This app is originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber).
@ -79,9 +99,8 @@ This app is originally made by [@AwaisKing](https://github.com/AwaisKing) on [Gi
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/instagrabber)
[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/instagrabber)
[![LGTM Grade](https://img.shields.io/lgtm/grade/java/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/instagrabber)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f87cac1fbf674888b00bd91bc5eccce0)](https://app.codacy.com/manual/austinhuang0131/instagrabber)
[![CodeFactor](https://www.codefactor.io/repository/github/austinhuang0131/instagrabber/badge)](https://www.codefactor.io/repository/github/austinhuang0131/instagrabber)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e9cfcb7733f8477d92e5c0f30cac137a)](https://www.codacy.com/manual/austinhuang0131/instagrabber)
[![Crowdin](https://badges.crowdin.net/instagrabber/localized.svg)](https://crowdin.com/project/instagrabber)
[![forthebadge](https://forthebadge.com/images/badges/made-with-java.svg)](https://forthebadge.com)[![forthebadge](https://forthebadge.com/images/badges/built-for-android.svg)](https://forthebadge.com) [![gplv3](https://www.gnu.org/graphics/gplv3-with-text-136x68.png)](https://www.gnu.org/licenses/gpl-3.0.html)
<sub>1. For anonymous users only, does not apply to users already logged in, and must be explicitly enabled in Settings. 2. Default enabled, but can be disabled. 3. Shameless plug: If you do not have an Android device but wants to do that, read [this](https://austinhuang.me/instagram-compare).</sub>

View File

@ -119,21 +119,25 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
} else if (which == 3) {
Utils.copyText(this, commentModel.getText().toString());
} else if (which == 4) {
focus = commentsBinding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888);
commentsBinding.commentCancelParent.setVisibility(View.VISIBLE);
String mention = "@"+profileModel.getUsername()+" ";
commentsBinding.commentText.setText(mention);
commentsBinding.commentText.requestFocus();
commentsBinding.commentText.setSelection(mention.length());
commentsBinding.commentText.postDelayed(new Runnable(){
@Override
public void run(){
imm = (InputMethodManager) getSystemService(getApplicationContext().INPUT_METHOD_SERVICE);
imm.showSoftInput(commentsBinding.commentText, 0);
}
if (commentModel == null) {
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
else {
focus = commentsBinding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888);
commentsBinding.commentCancelParent.setVisibility(View.VISIBLE);
String mention = "@" + profileModel.getUsername() + " ";
commentsBinding.commentText.setText(mention);
commentsBinding.commentText.requestFocus();
commentsBinding.commentText.setSelection(mention.length());
commentsBinding.commentText.postDelayed(new Runnable() {
@Override
public void run() {
imm = (InputMethodManager) getSystemService(getApplicationContext().INPUT_METHOD_SERVICE);
imm.showSoftInput(commentsBinding.commentText, 0);
}
}, 200);
}
,200);
} else if (which == 5) {
new CommentAction().execute((commentModel.getLiked() ? "unlike/" : "like/")+commentModel.getId());
} else if (which == 6) {

View File

@ -222,11 +222,9 @@ public final class PostViewer extends BaseLanguageActivity {
viewerBinding.bottomPanel.commentsCount.setText(String.valueOf(commentsCount));
viewerBinding.bottomPanel.btnComments.setVisibility(View.VISIBLE);
postShortCode = result[0].getShortCode();
viewerBinding.bottomPanel.btnComments.setOnClickListener(v ->
startActivityForResult(new Intent(this, CommentsViewer.class)
.putExtra(Constants.EXTRAS_SHORTCODE, postShortCode)
.putExtra(Constants.EXTRAS_SHORTCODE, postModel.getShortCode())
.putExtra(Constants.EXTRAS_POST, viewerPostModel.getPostId())
.putExtra(Constants.EXTRAS_USER, postUserId), 6969));
viewerBinding.bottomPanel.btnComments.setClickable(true);
@ -243,7 +241,8 @@ public final class PostViewer extends BaseLanguageActivity {
}
}
setupPostInfoBar("@" + viewerPostModel.getUsername(), viewerPostModel.getItemType(), viewerPostModel.getLocation());
setupPostInfoBar("@" + viewerPostModel.getUsername(), viewerPostModel.getItemType(),
viewerPostModel.getLocationName(), viewerPostModel.getLocation());
postCaption = postModel.getPostCaption();
viewerCaptionParent.setVisibility(View.VISIBLE);
@ -302,7 +301,7 @@ public final class PostViewer extends BaseLanguageActivity {
final boolean postIdNull = postModel.getPostId() == null;
if (!postIdNull)
setupPostInfoBar(intent.getStringExtra(Constants.EXTRAS_USER), postModel.getItemType(), null);
setupPostInfoBar(intent.getStringExtra(Constants.EXTRAS_USER), postModel.getItemType(), null, null);
isFromShare = postModel.getPosition() == -1 || postIdNull;
@ -583,9 +582,9 @@ public final class PostViewer extends BaseLanguageActivity {
viewerBinding.bottomPanel.viewerCaption.setMentionClickListener(null);
viewerBinding.bottomPanel.viewerCaption.setText(postCaption);
}
setupPostInfoBar("@" + viewerPostModel.getUsername(), viewerPostModel.getItemType(),
viewerPostModel.getLocation());
viewerPostModel.getLocationName(), viewerPostModel.getLocation());
if (postModel instanceof PostModel) {
final PostModel postModel = (PostModel) this.postModel;
@ -633,7 +632,7 @@ public final class PostViewer extends BaseLanguageActivity {
}
}
private void setupPostInfoBar(final String from, final MediaItemType mediaItemType, final JSONObject location) {
private void setupPostInfoBar(final String from, final MediaItemType mediaItemType, final String locationName, final String location) {
if (prevUsername == null || !prevUsername.equals(from)) {
// viewerBinding.topPanel.ivProfilePic.setImageBitmap(null);
// viewerBinding.topPanel.ivProfilePic.setImageDrawable(null);
@ -708,8 +707,8 @@ public final class PostViewer extends BaseLanguageActivity {
));
} else {
viewerBinding.topPanel.location.setVisibility(View.VISIBLE);
viewerBinding.topPanel.location.setText(location.optString("name"));
viewerBinding.topPanel.location.setOnClickListener(v -> searchUsername(location.optString("id") + "/" + location.optString("slug")));
viewerBinding.topPanel.location.setText(locationName);
viewerBinding.topPanel.location.setOnClickListener(v -> searchUsername(location));
viewerBinding.topPanel.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));

View File

@ -106,7 +106,8 @@ public final class DirectMessageItemsAdapter extends ListAdapter<DirectItemModel
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageReelShareViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
}
case MEDIA_SHARE: {
case MEDIA_SHARE:
case CLIP: {
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageMediaShareViewHolder(baseBinding, binding, onClickListener);
}

View File

@ -71,6 +71,7 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
case MEDIA:
case MEDIA_SHARE:
case RAVEN_MEDIA:
case CLIP:
messageText = context.getString(R.string.direct_messages_sent_media);
break;
case ACTION_LOG:

View File

@ -148,7 +148,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" +
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
try {

View File

@ -164,7 +164,10 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
null, null, null,
node.optLong("video_view_count", -1), -1, false, false,
feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
feedItem.optJSONObject("location"));
feedItem.isNull("location") ? null : feedItem.getJSONObject("location").optString("name"),
feedItem.isNull("location") ? null :
(feedItem.getJSONObject("location").optString("id") + "/" +
feedItem.getJSONObject("location").optString("slug")));
sliderItems[j].setSliderDisplayUrl(node.getString("display_url"));
}

View File

@ -95,7 +95,10 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
isVideo && media.has("video_view_count") ? media.getLong("video_view_count") : -1,
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
media.getJSONObject("edge_media_preview_like").getLong("count"),
media.optJSONObject("location"));
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModel.setCommentsCount(commentsCount);
@ -120,7 +123,10 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
isChildVideo && node.has("video_view_count") ? node.getLong("video_view_count") : -1,
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
media.getJSONObject("edge_media_preview_like").getLong("count"),
media.optJSONObject("location"));
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModels[i].setSliderDisplayUrl(node.getString("display_url"));
Utils.checkExistence(downloadDir, customDir, true, postModels[i]);

View File

@ -87,7 +87,10 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
isVideo && media.has("view_count") ? media.getLong("view_count") : -1,
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"),
media.getLong("like_count"),
media.optJSONObject("location"));
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModel.setCommentsCount(commentsCount);
@ -114,7 +117,10 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
-1,
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"),
media.getLong("like_count"),
media.optJSONObject("location"));
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModels[i].setSliderDisplayUrl(Utils.getHighQualityImage(node));
Utils.checkExistence(downloadDir, customDir, true, postModels[i]);

View File

@ -16,7 +16,9 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -84,6 +86,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
conn.setInstanceFollowRedirects(true);
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT);
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
@ -121,6 +124,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
models[i].setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id"));
}
// assuming everything is spotify
if (!data.isNull("story_app_attribution"))
models[i].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
@ -136,6 +140,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
tappableObject.optInt("viewer_vote", -1)
));
}
if (data.has("story_questions")) {
JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0).optJSONObject("question_sticker");
if (tappableObject != null && !tappableObject.getString("question_type").equals("music"))
@ -144,6 +149,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
tappableObject.getString("question")
));
}
if (data.has("story_quizs")) {
JSONObject tappableObject = data.getJSONArray("story_quizs").getJSONObject(0).optJSONObject("quiz_sticker");
if (tappableObject != null) {
@ -164,6 +170,18 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
));
}
}
if (data.has("story_cta") && data.has("link_text")) {
JSONObject tappableObject = data.getJSONArray("story_cta").getJSONObject(0).getJSONArray("links").getJSONObject(0);
String swipeUpUrl = tappableObject.getString("webUri");
if (swipeUpUrl.startsWith("http")) {
models[i].setSwipeUp(new SwipeUpModel(
swipeUpUrl,
data.getString("link_text")
));
}
}
JSONArray hashtags = data.optJSONArray("story_hashtags");
JSONArray locations = data.optJSONArray("story_locations");
JSONArray atmarks = data.optJSONArray("reel_mentions");

View File

@ -47,7 +47,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
private String endCursor;
private AsyncTask<Void, Void, InboxModel> currentlyRunning;
private InboxThreadModelListViewModel listViewModel;
public static boolean afterLeave = false;
public static boolean refreshPlease = false;
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() {
@Override
@ -121,9 +121,9 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
@Override
public void onResume() {
super.onResume();
if (afterLeave) {
if (refreshPlease) {
onRefresh();
afterLeave = false;
refreshPlease = false;
}
}

View File

@ -199,7 +199,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
titleText.clearFocus();
}
else if (action.equals("leave")) {
DirectMessageInboxFragment.afterLeave = true;
DirectMessageInboxFragment.refreshPlease = true;
NavHostFragment.findNavController(DirectMessageSettingsFragment.this).popBackStack(R.id.directMessagesInboxFragment, false);
}
}

View File

@ -66,6 +66,7 @@ import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.UserInboxDirection;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
@ -208,6 +209,7 @@ public class DirectMessageThreadFragment extends Fragment {
final DirectItemType itemType = directItemModel.getItemType();
switch (itemType) {
case MEDIA_SHARE:
case CLIP:
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false)));
break;
@ -224,8 +226,16 @@ public class DirectMessageThreadFragment extends Fragment {
case RAVEN_MEDIA:
case MEDIA:
final ProfileModel user = getUser(directItemModel.getUserId());
Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia()));
Toast.makeText(requireContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
final DirectItemModel.DirectItemMediaModel selectedItem =
itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia();
final String url = selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? selectedItem.getVideoUrl() : selectedItem.getThumbUrl();
if (url == null) {
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
else {
Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, selectedItem);
Toast.makeText(requireContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
}
break;
case STORY_SHARE:
if (directItemModel.getReelShare() != null) {
@ -275,6 +285,7 @@ public class DirectMessageThreadFragment extends Fragment {
switch (itemType) {
case MEDIA_SHARE:
case CLIP:
firstOption = R.string.view_post;
break;
case LINK:
@ -376,6 +387,7 @@ public class DirectMessageThreadFragment extends Fragment {
}
directItemModel.setLiked();
}
DirectMessageInboxFragment.refreshPlease = true;
hasSentSomething = true;
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});

View File

@ -6,6 +6,7 @@ import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
public final class StoryModel implements Serializable {
private final String storyMediaId, storyUrl, username, userId;
@ -15,6 +16,7 @@ public final class StoryModel implements Serializable {
private PollModel poll;
private QuestionModel question;
private QuizModel quiz;
private SwipeUpModel swipeUp;
private String[] mentions;
private int position;
private boolean isCurrentSlide = false, canReply = false;
@ -66,6 +68,8 @@ public final class StoryModel implements Serializable {
return quiz;
}
public SwipeUpModel getSwipeUp() { return swipeUp;}
public String[] getMentions() {
return mentions;
}
@ -106,6 +110,10 @@ public final class StoryModel implements Serializable {
this.mentions = mentions;
}
public void setSwipeUp(final SwipeUpModel swipeUp) {
this.swipeUp = swipeUp;
}
public void setPosition(final int position) {
this.position = position;
}

View File

@ -5,8 +5,7 @@ import org.json.JSONObject;
import awais.instagrabber.models.enums.MediaItemType;
public final class ViewerPostModel extends BasePostModel {
protected final String username;
protected final JSONObject location;
protected final String username, locationName, location;
protected final long videoViews;
protected String sliderDisplayUrl;
protected long commentsCount, likes;
@ -14,7 +13,7 @@ public final class ViewerPostModel extends BasePostModel {
public ViewerPostModel(final MediaItemType itemType, final String postId, final String displayUrl, final String shortCode,
final String postCaption, final String username, final long videoViews, final long timestamp,
boolean liked, boolean bookmarked, long likes, final JSONObject location) {
boolean liked, boolean bookmarked, long likes, final String locationName, final String location) {
this.itemType = itemType;
this.postId = postId;
this.displayUrl = displayUrl;
@ -26,6 +25,7 @@ public final class ViewerPostModel extends BasePostModel {
this.liked = liked;
this.likes = likes;
this.bookmarked = bookmarked;
this.locationName = locationName;
this.location = location;
}
@ -41,7 +41,11 @@ public final class ViewerPostModel extends BasePostModel {
return username;
}
public JSONObject getLocation() {
public String getLocationName() {
return locationName;
}
public String getLocation() {
return location;
}

View File

@ -18,7 +18,8 @@ public enum DirectItemType implements Serializable {
REEL_SHARE(11),
ACTION_LOG(12),
PLACEHOLDER(13),
STORY_SHARE(14);
STORY_SHARE(14),
CLIP(15); // clip is just media_share but reel
private final int id;
private static Map<Integer, DirectItemType> map = new HashMap<>();

View File

@ -0,0 +1,20 @@
package awais.instagrabber.models.stickers;
import java.io.Serializable;
public final class SwipeUpModel implements Serializable {
private final String url, text;
public SwipeUpModel(final String url, final String text) {
this.url = url;
this.text = text;
}
public String getUrl() {
return url;
}
public String getText() {
return text;
}
}

View File

@ -144,10 +144,10 @@ public final class Utils {
final URI uri1 = new URI("https://instagram.com");
final URI uri2 = new URI("https://instagram.com/");
final URI uri3 = new URI("https://i.instagram.com/");
for (final String cookie : cookieRaw.split(";")) {
for (final String cookie : cookieRaw.split("; ")) {
final String[] strings = cookie.split("=", 2);
final HttpCookie httpCookie = new HttpCookie(strings[0].trim(), strings[1].trim());
httpCookie.setDomain("instagram.com");
httpCookie.setDomain(".instagram.com");
httpCookie.setPath("/");
httpCookie.setVersion(0);
cookieStore.add(uri1, httpCookie);
@ -427,7 +427,7 @@ public final class Utils {
if (userObj != null) {
user = new ProfileModel(
userObj.getBoolean("is_private"),
false, // temporary
false,
userObj.optBoolean("is_verified"),
String.valueOf(userObj.get("pk")),
userObj.getString("username"),
@ -468,6 +468,7 @@ public final class Utils {
if ("animated_media".equals(itemType)) return DirectItemType.ANIMATED_MEDIA;
if ("voice_media".equals(itemType)) return DirectItemType.VOICE_MEDIA;
if ("story_share".equals(itemType)) return DirectItemType.STORY_SHARE;
if ("clip".equals(itemType)) return DirectItemType.CLIP;
return DirectItemType.TEXT;
}
@ -708,6 +709,11 @@ public final class Utils {
directMedia = getDirectMediaModel(itemObject.getJSONObject("media_share"));
break;
case CLIP:
Log.d("austin_debug", "clip: "+itemObject.getJSONObject("clip").getJSONObject("clip"));
directMedia = getDirectMediaModel(itemObject.getJSONObject("clip").getJSONObject("clip"));
break;
case MEDIA:
directMedia = getDirectMediaModel(itemObject.optJSONObject("media"));
break;
@ -999,10 +1005,10 @@ public final class Utils {
}
public static void dmDownload(@NonNull final Context context, @Nullable final String username, final DownloadMethod method,
final List<? extends DirectItemMediaModel> itemsToDownload) {
final DirectItemMediaModel itemsToDownload) {
if (settingsHelper == null) settingsHelper = new SettingsHelper(context);
if (itemsToDownload == null || itemsToDownload.size() < 1) return;
if (itemsToDownload == null) return;
if (ContextCompat.checkSelfPermission(context, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
dmDownloadImpl(context, username, method, itemsToDownload);
@ -1011,7 +1017,7 @@ public final class Utils {
}
private static void dmDownloadImpl(@NonNull final Context context, @Nullable final String username,
final DownloadMethod method, final List<? extends DirectItemMediaModel> itemsToDownload) {
final DownloadMethod method, final DirectItemMediaModel selectedItem) {
File dir = new File(Environment.getExternalStorageDirectory(), "Download");
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
@ -1023,22 +1029,11 @@ public final class Utils {
dir = new File(dir, username);
if (dir.exists() || dir.mkdirs()) {
final MainActivity mainActivity = method != DownloadMethod.DOWNLOAD_FEED && context instanceof MainActivity ? (MainActivity) context : null;
final int itemsToDownloadSize = itemsToDownload.size();
final File finalDir = dir;
for (int i = itemsToDownloadSize - 1; i >= 0; i--) {
final DirectItemMediaModel selectedItem = itemsToDownload.get(i);
if (mainActivity == null) {
new DownloadAsync(context,
selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? selectedItem.getVideoUrl() : selectedItem.getThumbUrl(),
getDownloadSaveFileDm(finalDir, selectedItem, ""),
null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
new DownloadAsync(context,
selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO ? selectedItem.getVideoUrl() : selectedItem.getThumbUrl(),
getDownloadSaveFileDm(finalDir, selectedItem, ""),
null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else
Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show();
}
@ -1256,7 +1251,7 @@ public final class Utils {
final int endIndex = (spaceIndex != -1 ? spaceIndex : length);
final String extractUrl = url.substring(startIndex, Math.min(length, endIndex) - 1);
final String extractUrl = url.substring(startIndex, Math.min(length, endIndex));
final SpannableString spannableString = new SpannableString(url);
spannableString.setSpan(new URLSpan(extractUrl), startIndex, endIndex, 0);

View File

@ -85,6 +85,15 @@
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"

View File

@ -46,6 +46,7 @@
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/feed_text_primary_color"
android:textSize="15sp" />
tools:text="location" />
</RelativeLayout>