[
  {
    "path": ".all-contributorsrc",
    "content": "{\n  \"files\": [\n    \"README.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"badgeTemplate\": \"[![All Contributors](https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg)](#contributors)\",\n  \"contributors\": [\n    {\n      \"login\": \"austinhuang0131\",\n      \"name\": \"Austin Huang\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/16656689\",\n      \"profile\": \"https://austinhuang.me\",\n      \"contributions\": [\n        \"code\",\n        \"doc\",\n        \"question\",\n        \"translation\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"ammargitham\",\n      \"name\": \"Ammar Githam\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/8017365\",\n      \"profile\": \"https://github.com/ammargitham\",\n      \"contributions\": [\n        \"code\",\n        \"design\",\n        \"ideas\",\n        \"maintenance\",\n        \"question\"\n      ]\n    },\n    {\n      \"login\": \"zerrium\",\n      \"name\": \"Zerrium\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/58355441?v=4\",\n      \"profile\": \"https://github.com/zerrium\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"junhuicoding\",\n      \"name\": \"Chua Jun Hui\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/54289027?v=4\",\n      \"profile\": \"https://github.com/junhuicoding\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"andersonvom\",\n      \"name\": \"Anderson Mesquita\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/69922?v=4\",\n      \"profile\": \"https://github.com/andersonvom\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"MeLlamoPablo\",\n      \"name\": \"Pablo Rodríguez\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/11708035?v=4\",\n      \"profile\": \"https://github.com/MeLlamoPablo\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"tcely\",\n      \"name\": \"tcely\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/138864?v=4\",\n      \"profile\": \"https://github.com/tcely\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Vonter\",\n      \"name\": \"Vonter\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/25414711?v=4\",\n      \"profile\": \"https://github.com/Vonter\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"raniapl\",\n      \"name\": \"Rania Pilioura\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/56370534?v=4\",\n      \"profile\": \"https://github.com/raniapl\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"stamatiap\",\n      \"name\": \"Stamatia Papageorgiou\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/57223967?v=4\",\n      \"profile\": \"https://github.com/stamatiap\",\n      \"contributions\": [\n        \"code\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Zopieux\",\n      \"name\": \"Alexandre Macabies\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/81353?v=4\",\n      \"profile\": \"https://github.com/Zopieux\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"vojta-horanek\",\n      \"name\": \"Vojtěch Hořánek\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/12630566?v=4\",\n      \"profile\": \"https://vojtechh.eu/\",\n      \"contributions\": [\n        \"code\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"The-EDev\",\n      \"name\": \"Farook Al-Sammarraie\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/60552923?v=4\",\n      \"profile\": \"https://github.com/The-EDev\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"snajdovski\",\n      \"name\": \"Stefan Najdovski\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/42580385?v=4\",\n      \"profile\": \"https://snajdovski.github.io\",\n      \"contributions\": [\n        \"design\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"CrazyMarvin\",\n      \"name\": \"CrazyMarvin\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/15004217?v=4\",\n      \"profile\": \"https://github.com/CrazyMarvin\",\n      \"contributions\": [\n        \"financial\"\n      ]\n    },\n    {\n      \"login\": \"KevinNThomas\",\n      \"name\": \"Kevin Thomas\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/15370181\",\n      \"profile\": \"http://kevinthomas.dev\",\n      \"contributions\": [\n        \"financial\"\n      ]\n    },\n    {\n      \"login\": \"Martin5001\",\n      \"name\": \"Martin Krejčí\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/35201200?v=4\",\n      \"profile\": \"https://github.com/Martin5001\",\n      \"contributions\": [\n        \"bug\",\n        \"ideas\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Shadowspear123\",\n      \"name\": \"Shadowspear123\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/50462281\",\n      \"profile\": \"https://github.com/Shadowspear123\",\n      \"contributions\": [\n        \"blog\",\n        \"bug\",\n        \"ideas\",\n        \"question\"\n      ]\n    },\n    {\n      \"login\": \"RickyM7\",\n      \"name\": \"Ricardo\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/24703825?v=4\",\n      \"profile\": \"https://github.com/RickyM7\",\n      \"contributions\": [\n        \"bug\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Akrai\",\n      \"name\": \"Akrai\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/5624597?v=4\",\n      \"profile\": \"https://github.com/Akrai\",\n      \"contributions\": [\n        \"ideas\",\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"avtkal\",\n      \"name\": \"avtkal\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/63205014?v=4\",\n      \"profile\": \"https://github.com/avtkal\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"cizordj\",\n      \"name\": \"Cézar Augusto\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/32869222?v=4\",\n      \"profile\": \"https://github.com/cizordj\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"dimitrist19\",\n      \"name\": \"Dimitris T\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/56406468?v=4\",\n      \"profile\": \"https://github.com/dimitrist19\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"farzadx\",\n      \"name\": \"farzadx\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/70059397?v=4\",\n      \"profile\": \"https://github.com/farzadx\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"faydin\",\n      \"name\": \"Fatih Aydın\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/22706676?v=4\",\n      \"profile\": \"https://github.com/faydin\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"fouze555\",\n      \"name\": \"fouze555\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/71935341?v=4\",\n      \"profile\": \"https://github.com/fouze555\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Galang23\",\n      \"name\": \"Galang23\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/13700948\",\n      \"profile\": \"https://github.com/Galang23\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"initdebugs\",\n      \"name\": \"Initdebugs\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/75781464?v=4\",\n      \"profile\": \"https://github.com/initdebugs\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"CrafterSvK\",\n      \"name\": \"Jakub Janek\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/8365659?v=4\",\n      \"profile\": \"https://janek.xyz/\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"GenosseFlosse\",\n      \"name\": \"GenosseFlosse\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/59205524?v=4\",\n      \"profile\": \"https://github.com/GenosseFlosse\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"kernoeb\",\n      \"name\": \"kernoeb\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/24623168\",\n      \"profile\": \"https://becauseofprog.fr/\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Lego8486\",\n      \"name\": \"Ten_Lego\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/47414485\",\n      \"profile\": \"https://github.com/Lego8486\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"MoaufmKlo\",\n      \"name\": \"MoaufmKlo\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/45636897\",\n      \"profile\": \"https://github.com/MoaufmKlo\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"nalinalini\",\n      \"name\": \"nalinalini\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/65640431?v=4\",\n      \"profile\": \"https://github.com/nalinalini\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"peterge1998\",\n      \"name\": \"peterge1998\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/47355238\",\n      \"profile\": \"https://github.com/peterge1998\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"PierreM0\",\n      \"name\": \"PierreM0\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/71077853?v=4\",\n      \"profile\": \"https://github.com/PierreM0\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Pyrobauve\",\n      \"name\": \"Pyrobauve\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/48654473?v=4\",\n      \"profile\": \"https://github.com/Pyrobauve\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"RAMAR-RAR\",\n      \"name\": \"RAMAR-RAR\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/47423745\",\n      \"profile\": \"https://github.com/RAMAR-RAR\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"rohang02\",\n      \"name\": \"rohang02\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/47921164?v=4\",\n      \"profile\": \"https://github.com/rohang02\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"retiolus\",\n      \"name\": \"retiolus\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/65604466?v=4\",\n      \"profile\": \"https://github.com/retiolus\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"rex07\",\n      \"name\": \"Rex_sa\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/13156001?v=4\",\n      \"profile\": \"https://github.com/rex07\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"rikishi0071\",\n      \"name\": \"rikishi0071\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/18183855?v=4\",\n      \"profile\": \"https://github.com/rikishi0071\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"sandboiii\",\n      \"name\": \"Alexey Peschany\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/17468894?v=4\",\n      \"profile\": \"https://gitlab.com/sandboiii\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Sitavi\",\n      \"name\": \"Sitavi\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/80586127?v=4\",\n      \"profile\": \"https://github.com/Sitavi\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Still34\",\n      \"name\": \"Still Hsu\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/5843208?v=4\",\n      \"profile\": \"https://stillu.cc/\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"Umeaboy\",\n      \"name\": \"Kristoffer Grundström\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/714473?v=4\",\n      \"profile\": \"https://github.com/Umeaboy\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"wagnim\",\n      \"name\": \"wagnim\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/30241419\",\n      \"profile\": \"https://github.com/wagnim\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"wokija\",\n      \"name\": \"wokija\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/14982166?v=4\",\n      \"profile\": \"https://github.com/wokija\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"ysakamoto\",\n      \"name\": \"ysakamoto\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/1331642?v=4\",\n      \"profile\": \"https://github.com/ysakamoto\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"ZDVokoun\",\n      \"name\": \"ZDVokoun\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/76393152?v=4\",\n      \"profile\": \"https://github.com/ZDVokoun\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    },\n    {\n      \"login\": \"2hot2exist\",\n      \"name\": \"2hot2exist\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/84233003?v=4\",\n      \"profile\": \"https://github.com/2hot2exist\",\n      \"contributions\": [\n        \"translation\"\n      ]\n    }\n  ],\n  \"contributorsPerLine\": 6,\n  \"projectName\": \"barinsta\",\n  \"projectOwner\": \"austinhuang0131\",\n  \"repoType\": \"github\",\n  \"repoHost\": \"https://github.com\",\n  \"skipCi\": true,\n  \"types\": {\n    \"translation\": {\n      \"symbol\": \"🌍\",\n      \"description\": \"Translation\",\n      \"link\": \"https://crowdin.com/project/instagrabber\"\n    }\n  },\n  \"commitConvention\": \"none\"\n}\n"
  },
  {
    "path": ".codebeatsettings",
    "content": "{\n  \"JAVA\": {\n    \"TOO_MANY_IVARS\": [8, 10, 20, 30]\n  }\n}"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nThis project adheres to No Code of Conduct.  We are all adults.  We accept anyone's contributions.  Nothing else matters.\n\nFor more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage."
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "## WARNING\n\n* 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).\n  * Some people have asked me about publishing a \"commercial\" fork that serves ads. However, if you do properly comply with GPLv3, users would realize that the original non-commercial version exists and, in turn, abandon your fork. And if you don't comply, you get copystriked.\n* Although publishing a fork is allowed by license, it is strongly discouraged to do so as it divides the effort and creates confusion (as well as for the reason above). 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.)\n\n## Contributing\n\nThank you for your interest in Barinsta!\n\nOur vision 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.\n\nAs we grow in popularity, it becomes apparent that we are short on hands. Every contribution counts!\n\n## I want to help coding it!\n\nGreat!\n\nGenerally, 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.)\n\nAs 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.\n\nCheck errors are for reference only. Try to minimize them, but usually they don't make a big difference.\n\n**NEVER touch the l10n-master branch.** It's automatically managed by Crowdin.\n\nThe legacy branch is no longer maintained.\n\n### I can't code Java, but I want to!\n\nFun fact: Austin took over this project and learned Java on the fly (I'm not joking, I only do JavaScript before taking this over).\n\nEven 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).\n\nIf you have questions, don't be afraid to ask for help from any current maintainer!\n\n## I found a bug!\n\n**Please read [FAQ](https://barinsta.austinhuang.me/en/latest/faq.html) first.**\n\nBugs are inevitable during active development, as nobody can cover all the possible test cases. \n\nYou can either email your crash dump to `barinsta@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://barinsta.austinhuang.me/en/latest/#contact-us) to remove it.\n\nGenerally, reporting bugs directly in support channels is not recommended, as they can be difficult to find.\n\n### I want to help... in some other way.\n\nYou can...\n\n* translate it [![badge](https://badges.crowdin.net/instagrabber/localized.svg)](https://crowdin.com/project/instagrabber)\n* 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))\n* star it [![stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://github.com/austinhuang0131/barinsta/stargazers)\n\nHappy contributing!\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: austinhuang0131\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: austinhuang\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: austinhuang\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: [\"https://austinhuang.me/donate\"]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/ban_report.md",
    "content": "---\nname: Ban report\nabout: If you suspect a ban by Instagram due to your usage of this app, you MUST report it.\ntitle: \"[BAN]\"\nlabels: BAN REPORT\nassignees: 'austinhuang0131'\n\n---\n\n<!-- Write \"x\" in the brackets to check -->\n\n## Answer honestly. All boxes must be checked for this report to be considered.\n\n- [ ] My app is on the latest version available on GitHub or F-Droid. I understand that any other version is not supported.\n- [ ] I certify that all actions I have performed on the app constitute human behaviour. I am using the app responsibly and in a way identical to using the official app. Specifically, I did not perform botting or automated key clicks.\n- [ ] I have considered other possible reasons for my ban, and I cannot find any substantial claim other than the usage of this app.\n- [ ] Instagram has indicated that this is a permanent ban, not a required password change or a temporary lock.\n\n## Answer honestly. Check accordingly to your situation.\n\n- [ ] I had prior rule violations on Instagram, specifically:\n- [ ] I have admitted the use of Barinsta on Instagram.\n- [ ] I have admitted the use of Barinsta to a friend who uses Instagram.\n- [ ] I have modified the source code of the app that I use, other than what is present in this repo. Specifically:\n\n## Describe your case, including your usage pattern, but without private information.\n\n## Provide your communication with Instagram, without private information of you.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: App crashing? You seeing a confusing error message? Report them here!\ntitle: \"[BUG]\"\nlabels: bug\nassignees: ''\n---\n\n<!-- Frequent reporters can ignore this template as long as it's clear and concise. -->\n\n- [ ] My app is *at least* at the current release version. I understand that any versions before that is not supported.\n- [ ] I have read [the FAQ](https://barinsta.austinhuang.me/en/latest/faq.html).\n\n<!--\nDescribe the bug here. Don't stress too much, but do include the key points.\n-->\n\n## Steps\n\n<!--\n1. ...\n2. ...\n3. See error...\n-->\n\n## Environment\n\n - Device: \n - Android version:\n\n## Additional info\n\n<!-- If you have a crash dump, paste it here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Community Chatrooms\n    url: https://barinsta.austinhuang.me/en/latest/chat.html\n    about: Chat with developers and users alike!\n  - name: /r/Barinsta\n    url: https://reddit.com/r/barinsta\n    about: Start a discussion on our subreddit!\n  - name: Repository Discussions\n    url: https://github.com/austinhuang0131/barinsta/discussions\n    about: Start a discussion in this repo!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Want new features? Ask them here!\ntitle: \"[FTR]\"\nlabels: enhancement\nassignees: ''\n\n---\n\n<!--\nLarge features will either take a lot of time or another maintainer\nJust remember to be patient, and if you have skills, try DIY\nAlso write title after [FTR]\n-->\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/issue_label_bot.yaml",
    "content": "label-alias:\n  bug: 'bug'\n  feature_request: 'enhancement'\n  question: 'question'\n"
  },
  {
    "path": ".github/workflows/github_nightly_release.yml",
    "content": "name: Github nightly\n\non:\n  schedule:\n    # * is a special character in YAML so you have to quote this string\n    - cron:  '27 10 * * *' # Everyday at 10:27:00\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    \n    - name: set up JDK 1.8\n      uses: actions/setup-java@v2\n      with:\n        distribution: 'zulu'\n        java-version: '8'\n\n    - name: Grant execute permission for gradlew\n      run: chmod +x gradlew\n    \n    - name: Build Github unsigned apk\n      run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre --project-prop split\n      \n    - name: Sign APK\n      uses: ammargitham/sign-android-release@v1.1.1\n      # ID used to access action output\n      id: sign_app\n      with:\n        releaseDirectory: app/build/outputs/apk/github/release\n        signingKeyBase64: ${{ secrets.SIGNING_KEY }}\n        alias: ${{ secrets.ALIAS }}\n        keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}\n        keyPassword: ${{ secrets.KEY_PASSWORD }}\n    \n    - name: Get current date and time\n      id: date\n      run: echo \"::set-output name=date::$(date +'%Y%m%d_%H%M%S')\"\n    \n    # Create artifact    \n    - name: Create apk artifact\n      uses: actions/upload-artifact@v2\n      with:\n        name: barinsta_nightly_${{ steps.date.outputs.date }}\n        # path: ${{steps.sign_app.outputs.signedReleaseFile}}\n        path: app/build/outputs/apk/github/release/*-signed.apk\n        \n    # Send success notification\n    - name: Send success Telegram notification\n      if: ${{ success() }}\n      uses: appleboy/telegram-action@master\n      with:\n        to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}\n        token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}\n        message: \"${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}\"\n        # document: ${{steps.sign_app.outputs.signedReleaseFile}}\n        document: app/build/outputs/apk/github/release/*-signed.apk\n    \n    # Send failure notification\n    - name: Send failure Telegram notification\n      if: ${{ failure() }}\n      uses: appleboy/telegram-action@master\n      with:\n        to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}\n        token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}\n        message: \"${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}\"\n"
  },
  {
    "path": ".github/workflows/github_pre_release.yml",
    "content": "name: Github pre-release\n\non: workflow_dispatch\n#   push:\n#     branches: [ master ]\n#   pull_request:\n#     branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n\n    - name: set up JDK 1.8\n      uses: actions/setup-java@v2\n      with:\n        distribution: 'zulu'\n        java-version: '8'\n\n    - name: Grant execute permission for gradlew\n      run: chmod +x gradlew\n\n    - name: Build Github unsigned pre-release apk\n      run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre --project-prop split\n\n    - name: Sign APK\n      uses: ammargitham/sign-android-release@v1.1.1\n      # ID used to access action output\n      id: sign_app\n      with:\n        releaseDirectory: app/build/outputs/apk/github/release\n        signingKeyBase64: ${{ secrets.SIGNING_KEY }}\n        alias: ${{ secrets.ALIAS }}\n        keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}\n        keyPassword: ${{ secrets.KEY_PASSWORD }}\n\n    - name: Get current date and time\n      id: date\n      run: echo \"::set-output name=date::$(date +'%Y%m%d_%H%M%S')\"\n\n    # Create artifact\n    - name: Create apk artifact\n      uses: actions/upload-artifact@v2\n      with:\n        name: barinsta_pre-release_${{ steps.date.outputs.date }}\n        # path: ${{steps.sign_app.outputs.signedReleaseFile}}\n        path: app/build/outputs/apk/github/release/*-signed.apk\n\n    # Send success notification\n    - name: Send success Telegram notification\n      if: ${{ success() }}\n      uses: appleboy/telegram-action@master\n      with:\n        to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}\n        token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}\n        message: \"${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}\"\n        # document: ${{steps.sign_app.outputs.signedReleaseFile}}\n        document: app/build/outputs/apk/github/release/*-signed.apk\n\n    # Send failure notification\n    - name: Send failure Telegram notification\n      if: ${{ failure() }}\n      uses: appleboy/telegram-action@master\n      with:\n        to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }}\n        token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }}\n        message: \"${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}\"\n"
  },
  {
    "path": ".github/workflows/label-bugs.yml",
    "content": "name: Label bugs\n\non:\n  issues:\n    types: [opened]\n\njobs:\n  add-labels:\n    runs-on: ubuntu-latest\n    if: contains(github.event.issue.body, 'New Trace collected:') == true\n    steps:\n      - name: Add labels\n        uses: actions-cool/issues-helper@v2.2.1\n        with:\n          actions: 'add-labels'\n          token: ${{ secrets.GITHUB_TOKEN }}\n          issue-number: ${{ github.event.issue.number }}\n          labels: 'bug'\n      - name: Remove runs\n        uses: GongT/cancel-previous-workflows@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          DELETE: 'yes'\n"
  },
  {
    "path": ".github/workflows/label-duplicates.yml",
    "content": "name: Label duplicates\n\non:\n  issue_comment:\n    types: [created]\n\njobs:\n  add-labels:\n    runs-on: ubuntu-latest\n    if: contains(github.event.comment.body, 'Duplicate of') == true\n    steps:\n      - name: Add labels\n        uses: actions-cool/issues-helper@v2.2.1\n        with:\n          actions: 'add-labels'\n          token: ${{ secrets.GITHUB_TOKEN }}\n          issue-number: ${{ github.event.issue.number }}\n          labels: 'duplicate'\n      - name: Remove runs\n        uses: GongT/cancel-previous-workflows@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          DELETE: 'yes'\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/markdown-navigator.xml\n/.idea/markdown-navigator-enh.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n/.idea/git_toolbox_prj.xml\n/.idea/dbnavigator.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\napp/release\n/sentry.properties\n/app/fdroid/\n/app/github/\n/repo\n/.fdroid.yml\n"
  },
  {
    "path": ".idea/.gitignore",
    "content": "# Default ignored files\n/shelf/\n/dictionaries/\n"
  },
  {
    "path": ".idea/.name",
    "content": "Barinsta"
  },
  {
    "path": ".idea/codeStyles",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectCodeStyleConfiguration\">\n    <code_scheme name=\"Project\" version=\"173\">\n      <codeStyleSettings language=\"XML\">\n        <indentOptions>\n          <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n        </indentOptions>\n        <arrangement>\n          <rules>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>xmlns:android</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>^$</XML_NAMESPACE>\n                  </AND>\n                </match>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>xmlns:.*</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>^$</XML_NAMESPACE>\n                  </AND>\n                </match>\n                <order>BY_NAME</order>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>.*:id</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                  </AND>\n                </match>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>.*:name</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                  </AND>\n                </match>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>name</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>^$</XML_NAMESPACE>\n                  </AND>\n                </match>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>style</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>^$</XML_NAMESPACE>\n                  </AND>\n                </match>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>.*</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>^$</XML_NAMESPACE>\n                  </AND>\n                </match>\n                <order>BY_NAME</order>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>.*</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                  </AND>\n                </match>\n                <order>ANDROID_ATTRIBUTE_ORDER</order>\n              </rule>\n            </section>\n            <section>\n              <rule>\n                <match>\n                  <AND>\n                    <NAME>.*</NAME>\n                    <XML_ATTRIBUTE />\n                    <XML_NAMESPACE>.*</XML_NAMESPACE>\n                  </AND>\n                </match>\n                <order>BY_NAME</order>\n              </rule>\n            </section>\n          </rules>\n        </arrangement>\n      </codeStyleSettings>\n    </code_scheme>\n  </component>\n</project>"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <bytecodeTargetLevel target=\"11\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleMigrationSettings\" migrationVersion=\"1\" />\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"testRunner\" value=\"PLATFORM\" />\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n        <option name=\"useQualifiedModuleNames\" value=\"true\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/inspectionProfiles/profiles_settings.xml",
    "content": "<component name=\"InspectionProjectProfileManager\">\n  <settings>\n    <option name=\"PROJECT_PROFILE\" value=\"Default\" />\n    <option name=\"USE_PROJECT_PROFILE\" value=\"false\" />\n    <version value=\"1.0\" />\n  </settings>\n</component>"
  },
  {
    "path": ".idea/jarRepositories.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RemoteRepositoriesConfiguration\">\n    <remote-repository>\n      <option name=\"id\" value=\"central\" />\n      <option name=\"name\" value=\"Maven Central repository\" />\n      <option name=\"url\" value=\"https://repo1.maven.org/maven2\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"jboss.community\" />\n      <option name=\"name\" value=\"JBoss Community repository\" />\n      <option name=\"url\" value=\"https://repository.jboss.org/nexus/content/repositories/public/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"MavenRepo\" />\n      <option name=\"name\" value=\"MavenRepo\" />\n      <option name=\"url\" value=\"https://repo.maven.apache.org/maven2/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"BintrayJCenter\" />\n      <option name=\"name\" value=\"BintrayJCenter\" />\n      <option name=\"url\" value=\"https://jcenter.bintray.com/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"maven\" />\n      <option name=\"name\" value=\"maven\" />\n      <option name=\"url\" value=\"https://jitpack.io\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"Google\" />\n      <option name=\"name\" value=\"Google\" />\n      <option name=\"url\" value=\"https://dl.google.com/dl/android/maven2/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"maven\" />\n      <option name=\"name\" value=\"maven\" />\n      <option name=\"url\" value=\"http://maven.geotoolkit.org/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"maven\" />\n      <option name=\"name\" value=\"maven\" />\n      <option name=\"url\" value=\"https://www.jitpack.io\" />\n    </remote-repository>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ExternalStorageConfigurationManager\" enabled=\"true\" />\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"androidx.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"androidx.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"12\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"javax.annotation.CheckForNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.Nullable\" />\n          <item index=\"6\" class=\"java.lang.String\" itemvalue=\"android.annotation.Nullable\" />\n          <item index=\"7\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNullable\" />\n          <item index=\"8\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.qual.Nullable\" />\n          <item index=\"9\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NullableDecl\" />\n          <item index=\"10\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NullableType\" />\n          <item index=\"11\" class=\"java.lang.String\" itemvalue=\"com.android.annotations.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"11\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.NonNull\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"android.annotation.NonNull\" />\n          <item index=\"6\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNonNull\" />\n          <item index=\"7\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.qual.NonNull\" />\n          <item index=\"8\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NonNullDecl\" />\n          <item index=\"9\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NonNullType\" />\n          <item index=\"10\" class=\"java.lang.String\" itemvalue=\"com.android.annotations.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_11\" default=\"true\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/render.experimental.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RenderSettings\">\n    <option name=\"showDecorations\" value=\"true\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations/app.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"app\" type=\"AndroidRunConfigurationType\" factoryName=\"Android App\" activateToolWindowBeforeRun=\"false\">\n    <module name=\"InstaGrabber.app\" />\n    <option name=\"DEPLOY\" value=\"true\" />\n    <option name=\"DEPLOY_APK_FROM_BUNDLE\" value=\"false\" />\n    <option name=\"DEPLOY_AS_INSTANT\" value=\"false\" />\n    <option name=\"ARTIFACT_NAME\" value=\"\" />\n    <option name=\"PM_INSTALL_OPTIONS\" value=\"\" />\n    <option name=\"ALL_USERS\" value=\"false\" />\n    <option name=\"DYNAMIC_FEATURES_DISABLED_LIST\" value=\"\" />\n    <option name=\"ACTIVITY_EXTRA_FLAGS\" value=\"\" />\n    <option name=\"MODE\" value=\"default_activity\" />\n    <option name=\"CLEAR_LOGCAT\" value=\"false\" />\n    <option name=\"SHOW_LOGCAT_AUTOMATICALLY\" value=\"false\" />\n    <option name=\"SKIP_NOOP_APK_INSTALLATIONS\" value=\"true\" />\n    <option name=\"FORCE_STOP_RUNNING_APP\" value=\"true\" />\n    <option name=\"TARGET_SELECTION_MODE\" value=\"DEVICE_AND_SNAPSHOT_COMBO_BOX\" />\n    <option name=\"SELECTED_CLOUD_MATRIX_CONFIGURATION_ID\" value=\"-1\" />\n    <option name=\"SELECTED_CLOUD_MATRIX_PROJECT_ID\" value=\"\" />\n    <option name=\"DEBUGGER_TYPE\" value=\"Auto\" />\n    <Auto>\n      <option name=\"USE_JAVA_AWARE_DEBUGGER\" value=\"false\" />\n      <option name=\"SHOW_STATIC_VARS\" value=\"true\" />\n      <option name=\"WORKING_DIR\" value=\"\" />\n      <option name=\"TARGET_LOGGING_CHANNELS\" value=\"lldb process:gdb-remote packets\" />\n      <option name=\"SHOW_OPTIMIZED_WARNING\" value=\"true\" />\n    </Auto>\n    <Hybrid>\n      <option name=\"USE_JAVA_AWARE_DEBUGGER\" value=\"false\" />\n      <option name=\"SHOW_STATIC_VARS\" value=\"true\" />\n      <option name=\"WORKING_DIR\" value=\"\" />\n      <option name=\"TARGET_LOGGING_CHANNELS\" value=\"lldb process:gdb-remote packets\" />\n      <option name=\"SHOW_OPTIMIZED_WARNING\" value=\"true\" />\n    </Hybrid>\n    <Java />\n    <Native>\n      <option name=\"USE_JAVA_AWARE_DEBUGGER\" value=\"false\" />\n      <option name=\"SHOW_STATIC_VARS\" value=\"true\" />\n      <option name=\"WORKING_DIR\" value=\"\" />\n      <option name=\"TARGET_LOGGING_CHANNELS\" value=\"lldb process:gdb-remote packets\" />\n      <option name=\"SHOW_OPTIMIZED_WARNING\" value=\"true\" />\n    </Native>\n    <Profilers>\n      <option name=\"ADVANCED_PROFILING_ENABLED\" value=\"false\" />\n      <option name=\"STARTUP_PROFILING_ENABLED\" value=\"false\" />\n      <option name=\"STARTUP_CPU_PROFILING_ENABLED\" value=\"false\" />\n      <option name=\"STARTUP_CPU_PROFILING_CONFIGURATION_NAME\" value=\"Sample Java Methods\" />\n      <option name=\"STARTUP_NATIVE_MEMORY_PROFILING_ENABLED\" value=\"false\" />\n      <option name=\"NATIVE_MEMORY_SAMPLE_RATE_BYTES\" value=\"2048\" />\n    </Profilers>\n    <option name=\"DEEP_LINK\" value=\"\" />\n    <option name=\"ACTIVITY_CLASS\" value=\"awais.instagrabber.activities.MainActivity\" />\n    <option name=\"SEARCH_ACTIVITY_IN_GLOBAL_SCOPE\" value=\"false\" />\n    <option name=\"SKIP_ACTIVITY_VALIDATION\" value=\"false\" />\n    <method v=\"2\">\n      <option name=\"Android.Gradle.BeforeRunTask\" enabled=\"true\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": ".project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>Barinsta</name>\n\t<comment>Project instagrabber created by Buildship.</comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.buildship.core.gradleprojectbuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.buildship.core.gradleprojectnature</nature>\n\t</natures>\n\t<filteredResources>\n\t\t<filter>\n\t\t\t<id>0</id>\n\t\t\t<name></name>\n\t\t\t<type>30</type>\n\t\t\t<matcher>\n\t\t\t\t<id>org.eclipse.core.resources.regexFilterMatcher</id>\n\t\t\t\t<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>\n\t\t\t</matcher>\n\t\t</filter>\n\t</filteredResources>\n</projectDescription>\n"
  },
  {
    "path": ".settings/org.eclipse.buildship.core.prefs",
    "content": "arguments=\nauto.sync=false\nbuild.scans.enabled=false\nconnection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)\nconnection.project.dir=\neclipse.preferences.version=1\ngradle.user.home=\njava.home=/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home\njvm.arguments=\noffline.mode=false\noverride.workspace.settings=true\nshow.console.view=true\nshow.executions.view=true\n"
  },
  {
    "path": "CHANGELOG",
    "content": "v15.9 to v16.5: https://gitlab.com/AwaisKing/instagrabber/-/releases\nFrom v16.6: https://github.com/austinhuang0131/barinsta/releases\n\nv15.9\nnote: there will be no F-Droid updates from this version (v15.9) and onward, download updates from repo's Releases page.\n\n+ added user stories in Feed\n+ added frame to currently showing slider item\n+ removed tap to pause/resume from Post viewer, replaced with controller\n+ fixed swipe not working when posts are opened from stories\n+ fixed comments not showing for slider items\n\nv15.8\n+ added user's website in profile\n+ fixed caption mentions length (@kernoeb)\n+ fixed some translations (@kernoeb)\n+ fixd feed captions merging with \"... more\"\n\nv15.4\n+ ADDED FRENCH AND SPANISH!!!\n+-> Huge thanks to @kernoeb (Telegram) for French translation and @sguinetti (GitLab) for Spanish translation!!\n\n+ added custom post time format support!\n+ fixed flickering after changing settings\n+ fixed posts not showing after searching from a private profile\n+ fixed stories and profile pictures not downloading in user folders even when option was enabled\n+ fixed issues with feed, discover and post viewer\n+ fixed search suggestions crashes\n\nv15.2\n+ fixed feed video not pausing when opened in post viewer\n+ added 1 new profile picture view mode\n+ added fields in LogCollector to better understand how things went wrong\n+ better comments, story, feed, discover and suggestion fetchers\n\nv15.0\n+ added support for Instagram.com urls! (:\n+ added \"Downloaded\" check on posts if they've already been downloaded (as per suggestion)\n+ fixed highlights scrolling issues\n+ fixed comments issues\n+ fixed posts weren't showing after searching anything from a private account\n+ fixed suggestions not showing sometimes\n+ fixed Stories viewer swipe issues\n+ fixed Import/Export dialog not showing on old phones\n+ added user's name in suggestions search list\n+ added comment likes in Comments viewer\n\n+ sending logs won't add empty logs to archive anymore!\n+ fixed no new line in logs\n+ handled Login WebView lifecycles\n+ a better way of handling highlight swipes\n+ removed useless code parts\n\nv14.5\n+ added changelog after update\n+ added swipe support in both Discover/Explore and Feed pages\n+ added Send Logs button in Settings to send logs when something doesn't work\n+ added clickable user profile in Post Viewer\n+ fixed weirdly collapsing toolbar when toolbar is shown at the top\n\nv14.0\n+ added theme selection support\n+ added import/export settings, favorites and logins functionality (thanks to Airikr [@edgren] on Telegram)\n+ added support for downloading slider items from User Feed page\n+ added support for usernames and hashtags in user's biography/about text\n+ added multiple selection in Discover/Explore page\n+ added post date for feed items\n+ added some more date formats\n+ copyable feed item caption (long tap)\n+ fixed late refresh indicator in Followers/Following comparison mode\n+ changed feed item size to squares\n+ fixed some caption text issues having mentions and hashtags\n+ removed clipboard listener (stopped working after some changes)\n+ added log collector for different crash scenarios\n\nv13.7\n+ fixed custom download folder selection issues\n\nv13.3\n+ added discover/explore page (only for logged in users)\n+ added function to remove IPTC tracking data from downloaded pictures (thanks to Airikr [@edgren] on Telegram)\n+ added multiple accounts support (quick access) and favorites (a suggestion from Saurabh on Telegram)\n+ added custom download folder option, you can select where to download posts (a suggestion from Airikr)\n+ added desktop mode toggle in Login activity (a suggestion from Eymen on Telegram)\n+ added post date in post viewer (a suggestion from W on Telegram)\n+ added post time format settings [ Settings > Post Time Settings ]\n+ fixed some icons and layouts\n+ removed color from slider items in feed\n+ removed useless methods and properties\n+ better way of handling multiple stories and posts (sliders) in post viewer\n+ tried to make notifications grouped together.. hope they work\n+ some other fixes and additions which i probably forgot, cause i'm a human ffs\n\nv13.0\n+ fixed crash when searching hashtags\n+ added lazy loading for hashtags\n+ added Show Feed option in Settings (feed may crash app on some phones)\n+ added null check in Download async\n+ better/adapatable icon\n+ fixed sheet dialog themes\n\nv12.7\n+ (probably) fixed inflating issue in some devices\n\nv12.5\n+ some small performance improvements\n\nv12.0\n+ added feed!! (only for logged in users)\n+ fixed multiple hashtags with no spaces between them\n+ stopped activity from recreating when nothing changed\n+ changed highlights to RecyclerView instead of HorizontalScrollView\n+ changed some numbers and precisions cause she left me on read\n\nv11.0\n+ added crash reporting library\n+ added mute/unmute for session\n+ better profile picture viewer + profile picture info\n+ better swipe gesture\n+ fixed mention and hashtag issues\n\nv10.0\nNOTE: YOU MAY NEED TO LOGIN TO VIEW PROFILES CAUSE OF INSTAGRAM CHANGES.\n+ added direct download multiple posts dialog\n+ fixed notification problems\n+ fixed some direct download problems\n+ fixed batch download and username folder not creating in some activities\n+ fixed update checker\n\nv9.0\n+ added search in comments viewer\n+ added settings to auto or lazy load posts\n+ added user & hashtag stack when back pressed\n+ added loading icon when loading posts\n+ profile info bar sticks to top\n+ fixed highlights and profile picture size in portrait and landscape mode\n+ fixed posts loading from other user when restarting app\n+ fixed posts size changing when scrolled\n+ users & hashtag search shows only users or hashtag when first char is @ or #\n+ scrolls to top when back pressed\n\nv8.0\n+ added pull-to-refresh layout in main posts, followers/following viewer\n+ added search in followers/following viewer\n+ added video views in post viewer\n+ added animation when showing highlights (if available)\n+ fixed long usernames not animating (marquee)\n+ fixed padding and size in portrait and landscape modes\n+ fixed accounts showing personal followers/following when account has 0 followers/following\n+ fixed double ripple when tapped on profile picture\n+ smaller story border around profile picture\n\nv7.0\n+ added comments viewer!!\n+ added highest quality post fetcher\n+ added loading indicator where it was missing before\n+ fixed highlight name alignment\n+ fixed swiping on posts opened via Share or link\n\nv6.0\n+ added story highlights!! (issue #5)\n+ added button to view posts posted in stories\n+ added verified badge for accounts\n+ fixed posts & story swiping issues\n+ fixed slow loading and stuff\n+ fixed different margins and sizes\n+ fixed activity not recreating after settings dialogs closed\n\nv5.0\n+ added followers / following checker\n+ added search view suggestions for usernames & hashtags\n+ added sliding profile container\n+ fixed batch download permission issue (issue #4)\n+ fixed some small screen panning issues\n+ fixed search view width\n+ fixed update checker\n\nv4.0\n+ fixed Login and Visit project page button codes.\n\nv3.0\n+ fixed posts merged from different accounts when searched while posts are loading!\n+ view stories (only if you're logged in)\n+ directly download posts\n+ choose between two pfp (profile picture) viewer methods\n+ fixed search box not showing up when toolbar is at bottom\n+ automatically checks for updates\n\nv2.0\n+ fixed Login crashes\n\nv1.0\n+ first ever changelog\n+ basic stuff like downloading profile pics, posts and copying captions and bio.\n+ batch download posts\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS"
  },
  {
    "path": "README.md",
    "content": "### THERE ARE CURRENTLY NO OFFICIAL GOOGLE PLAY RELEASES. PLEASE REPORT ANY OCCURRENCES TO US.\n\n<img src=\"./app/src/main/ic_launcher-round.png\" alt=\"Barinsta logo\" align=\"right\" width=\"20%\"/>\n\n# Barinsta\n\n[![Awesome Humane Tech](https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true)](https://github.com/humanetech-community/awesome-humane-tech)\n[![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com)\n[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)\n[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->\n[![All Contributors](https://img.shields.io/badge/all_contributors-51-orange.svg)](#contributors)\n<!-- ALL-CONTRIBUTORS-BADGE:END -->\n\nInstagram client; previously known as InstaGrabber.\n\nFor documentation, visit [Barinsta.AustinHuang.me](https://barinsta.austinhuang.me).\n\n## Download\n\n**By installing, you indicate your acceptance of [Terms of Service](https://barinsta.austinhuang.me/en/latest/tos.html) and [Privacy Policy](https://barinsta.austinhuang.me/en/latest/privacy.html).**\n\n<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>\n<a href=\"https://github.com/austinhuang0131/barinsta/releases/latest\"><img src=\"https://raw.githubusercontent.com/andOTP/andOTP/master/assets/badges/get-it-on-github.png\" height=\"75\"></a>\n\nVersion status: ![F-Droid](https://img.shields.io/f-droid/v/me.austinhuang.instagrabber.svg) vs. ![GitHub](https://img.shields.io/github/release/austinhuang0131/barinsta.svg?logo=github)\n\n## Screenshots\n\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\" alt=\"Profile\" width=\"15%\"/></a>\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/2.png\" alt=\"Post\" width=\"15%\"/></a>\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/3.png\" alt=\"Comments\" width=\"15%\"/></a>\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/4.png\" alt=\"Story\" width=\"15%\"/></a>\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/5.png\" alt=\"Hashtag\" width=\"15%\"/></a>\n<a href=\"https://github.com/austinhuang0131/instagrabber/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png\"><img src=\"./fastlane/metadata/android/en-US/images/phoneScreenshots/6.png\" alt=\"Discover Topics\" width=\"15%\"/></a>\n\n## We need maintainers!\n\nTo speed up development, we need more hands on deck. If you are proficient in Java and Android development, and are willing to perform such a public service, please [contact us](https://t.me/austinhuang).\n\n## Contact us\n\n* Use [GitHub issues](https://github.com/austinhuang0131/instagrabber/issues) when possible.\n* Email: [Barinsta@AustinHuang.me](mailto:barinsta@austinhuang.me?body=Please%20note%20that%20your%20email%20address%20and%20the%20entire%20content%20will%20be%20published%20onto%20GitHub%20issues.%20If%20you%20do%20not%20wish%20to%20do%20that%2C%20use%20other%20contact%20methods%20instead.) (Synced to GitHub issues)\n* Reddit: [![r/Barinsta](https://img.shields.io/reddit/subreddit-subscribers/Barinsta?style=social)](https://reddit.com/r/barinsta)\n* Chat (Bridged to each other): [![Matrix](https://img.shields.io/badge/Matrix-%23Barinsta:matrix.org-000000?logo=matrix)](https://matrix.to/#/#barinsta:matrix.org) [![Telegram](https://img.shields.io/badge/Telegram-@Barinsta__App-2CA5E0?logo=telegram)](https://t.me/barinsta_app) [![Discord](https://img.shields.io/badge/Discord-YtEDzN2-7289da?logo=discord&logoColor=white)](https://discord.gg/YtEDzN2)\n\n## Contributors\n\nProminent contributors are listed here in the [all-contributors](https://allcontributors.org/) specifications, see [emoji key](https://allcontributors.org/docs/en/emoji-key). [Want to contribute to Barinsta?](https://github.com/austinhuang0131/barinsta/blob/master/.github/CONTRIBUTING.md)\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tr>\n    <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>\n    <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>\n    <td align=\"center\"><a href=\"https://github.com/zerrium\"><img src=\"https://avatars.githubusercontent.com/u/58355441?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Zerrium</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=zerrium\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://github.com/junhuicoding\"><img src=\"https://avatars.githubusercontent.com/u/54289027?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Chua Jun Hui</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=junhuicoding\" title=\"Code\">💻</a></td>\n    <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>\n    <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>\n  </tr>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/tcely\"><img src=\"https://avatars.githubusercontent.com/u/138864?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>tcely</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=tcely\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://github.com/Vonter\"><img src=\"https://avatars.githubusercontent.com/u/25414711?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Vonter</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=Vonter\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://github.com/raniapl\"><img src=\"https://avatars.githubusercontent.com/u/56370534?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Rania Pilioura</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=raniapl\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://github.com/stamatiap\"><img src=\"https://avatars.githubusercontent.com/u/57223967?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Stamatia Papageorgiou</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=stamatiap\" title=\"Code\">💻</a> <a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <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>\n    <td align=\"center\"><a href=\"https://vojtechh.eu/\"><img src=\"https://avatars.githubusercontent.com/u/12630566?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Vojtěch Hořánek</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=vojta-horanek\" title=\"Code\">💻</a> <a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n  </tr>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/The-EDev\"><img src=\"https://avatars.githubusercontent.com/u/60552923?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Farook Al-Sammarraie</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/commits?author=The-EDev\" title=\"Code\">💻</a></td>\n    <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>\n    <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>\n    <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>\n    <td align=\"center\"><a href=\"https://github.com/Martin5001\"><img src=\"https://avatars.githubusercontent.com/u/35201200?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Martin Krejčí</b></sub></a><br /><a href=\"https://github.com/austinhuang0131/barinsta/issues?q=author%3AMartin5001\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-Martin5001\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <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>\n  </tr>\n  <tr>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n  </tr>\n  <tr>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n  </tr>\n  <tr>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n    <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>\n  </tr>\n  <tr>\n    <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>\n    <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>\n    <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>\n    <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>\n    <td align=\"center\"><a href=\"https://github.com/rex07\"><img src=\"https://avatars.githubusercontent.com/u/13156001?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Rex_sa</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <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>\n  </tr>\n  <tr>\n    <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>\n    <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>\n    <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>\n    <td align=\"center\"><a href=\"https://github.com/Umeaboy\"><img src=\"https://avatars.githubusercontent.com/u/714473?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Kristoffer Grundström</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <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>\n    <td align=\"center\"><a href=\"https://github.com/wokija\"><img src=\"https://avatars.githubusercontent.com/u/14982166?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>wokija</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n  </tr>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/ysakamoto\"><img src=\"https://avatars3.githubusercontent.com/u/1331642?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>ysakamoto</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <td align=\"center\"><a href=\"https://github.com/ZDVokoun\"><img src=\"https://avatars.githubusercontent.com/u/76393152?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>ZDVokoun</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n    <td align=\"center\"><a href=\"https://github.com/2hot2exist\"><img src=\"https://avatars.githubusercontent.com/u/84233003?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>2hot2exist</b></sub></a><br /><a href=\"https://crowdin.com/project/instagrabber\" title=\"Translation\">🌍</a></td>\n  </tr>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\n## License\n\nThis app's predecessor, InstaGrabber, was originally made by [@AwaisKing](https://github.com/AwaisKing) on [GitLab](https://gitlab.com/AwaisKing/instagrabber).\n\n    Barinsta\n    Copyright (C) 2020-2021  Austin Huang <im@austinhuang.me>\n                             Ammar Githam <ammar.githam@outlook.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nLogo by [Stefan Najdovski](https://snajdovski.github.io/). Used under license.\n\n[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/barinsta)\n[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/barinsta)\n[![LGTM Grade](https://img.shields.io/lgtm/grade/java/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/barinsta)\n[![CodeFactor](https://www.codefactor.io/repository/github/austinhuang0131/barinsta/badge)](https://www.codefactor.io/repository/github/austinhuang0131/barinsta)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e9cfcb7733f8477d92e5c0f30cac137a)](https://www.codacy.com/manual/austinhuang0131/instagrabber)\n[![Crowdin](https://badges.crowdin.net/instagrabber/localized.svg)](https://crowdin.com/project/instagrabber)\n\n[![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)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "If there is a security issue with the latest version, please let me know using GitHub issues (bug), or email `im [at] austinhuang [dot] me` if confidential. Use [this PGP key](https://github.com/austinhuang0131/austinhuang0131.github.io/blob/master/assets/key.asc) if you know how to.\n"
  },
  {
    "path": "app/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-12/\"/>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.buildship.core.gradleclasspathcontainer\"/>\n\t<classpathentry kind=\"output\" path=\"bin/default\"/>\n</classpath>\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>app</name>\n\t<comment>Project app created by Buildship.</comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.buildship.core.gradleprojectbuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.buildship.core.gradleprojectnature</nature>\n\t</natures>\n\t<filteredResources>\n\t\t<filter>\n\t\t\t<id>1600117114944</id>\n\t\t\t<name></name>\n\t\t\t<type>30</type>\n\t\t\t<matcher>\n\t\t\t\t<id>org.eclipse.core.resources.regexFilterMatcher</id>\n\t\t\t\t<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>\n\t\t\t</matcher>\n\t\t</filter>\n\t</filteredResources>\n</projectDescription>\n"
  },
  {
    "path": "app/.settings/org.eclipse.buildship.core.prefs",
    "content": "connection.project.dir=..\neclipse.preferences.version=1\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: \"androidx.navigation.safeargs\"\napply plugin: 'kotlin-kapt'\napply from: 'sentry.gradle'\n\ndef getGitHash = { ->\n    def stdout = new ByteArrayOutputStream()\n    exec {\n        commandLine 'git', 'rev-parse', '--short', 'HEAD'\n        standardOutput = stdout\n    }\n    return stdout.toString().trim()\n}\n\nandroid {\n    compileSdkVersion 30\n\n    defaultConfig {\n        applicationId 'me.austinhuang.instagrabber'\n\n        minSdkVersion 21\n        targetSdkVersion 30\n\n        versionCode 65\n        versionName '19.2.4'\n\n        multiDexEnabled true\n\n        vectorDrawables.useSupportLibrary = true\n        vectorDrawables.generatedDensities = []\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [\"room.schemaLocation\": \"$projectDir/schemas\".toString()]\n            }\n        }\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    sourceSets {\n        androidTest.assets.srcDirs += files(\"$projectDir/schemas\".toString())\n    }\n\n    compileOptions {\n        // Flag to enable support for the new language APIs\n        coreLibraryDesugaringEnabled true\n\n        targetCompatibility JavaVersion.VERSION_1_8\n        sourceCompatibility JavaVersion.VERSION_1_8\n    }\n\n    buildFeatures { viewBinding true }\n\n    aaptOptions { additionalParameters '--no-version-vectors' }\n\n    buildTypes {\n        debug {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    flavorDimensions \"repo\"\n\n    productFlavors {\n        github {\n            dimension \"repo\"\n            // versionNameSuffix \"-github\" // appended in assemble task\n            buildConfigField(\"String\", \"dsn\", SENTRY_DSN)\n            buildConfigField(\"boolean\", \"isPre\", \"false\")\n        }\n\n        fdroid {\n            dimension \"repo\"\n            versionNameSuffix \"-fdroid\"\n            buildConfigField(\"boolean\", \"isPre\", \"false\")\n        }\n    }\n\n    splits {\n        // Configures multiple APKs based on ABI.\n        abi {\n            // Enables building multiple APKs per ABI.\n            enable project.hasProperty(\"split\") && !gradle.startParameter.taskNames.isEmpty() && gradle.startParameter.taskNames.get(0).contains('Release')\n\n            // By default all ABIs are included, so use reset() and include to specify that we only\n            // want APKs for x86 and x86_64.\n\n            // Resets the list of ABIs that Gradle should create APKs for to none.\n            reset()\n\n            // Specifies a list of ABIs that Gradle should create APKs for.\n            include \"x86\", \"x86_64\", \"arm64-v8a\", \"armeabi-v7a\"\n\n            // Specifies that we want to also generate a universal APK that includes all ABIs.\n            universalApk true\n        }\n    }\n\n\n    android.applicationVariants.all { variant ->\n        if (variant.flavorName != \"github\") return\n        variant.outputs.all { output ->\n            def builtType = variant.buildType.name\n            def versionName = variant.versionName\n            // def versionCode = variant.versionCode\n            def flavor = variant.flavorName\n\n            def flavorBuiltType = \"${flavor}_${builtType}\"\n            def suffix\n            // For x86 and x86_64, the versionNames are already overridden\n            if (versionName.contains(flavorBuiltType)) {\n                suffix = \"${versionName}\"\n            } else {\n                suffix = \"${versionName}-${flavorBuiltType}\" // eg. 19.1.0-github_debug or release\n            }\n            if (builtType.toString() == 'release' && project.hasProperty(\"pre\")) {\n                buildConfigField(\"boolean\", \"isPre\", \"true\")\n\n                flavorBuiltType = \"${getGitHash()}-${flavor}\"\n\n                // For x86 and x86_64, the versionNames are already overridden\n                if (versionName.contains(flavorBuiltType)) {\n                    suffix = \"${versionName}\"\n                } else {\n                    // append latest commit short hash for pre-release\n                    suffix = \"${versionName}.${flavorBuiltType}\" // eg. 19.1.0.b123456-github\n                }\n            }\n\n            output.versionNameOverride = suffix\n            def abi = output.getFilter(com.android.build.OutputFile.ABI)\n            // println(abi + \", \" + versionName + \", \" + flavor + \", \" + builtType + \", \" + suffix)\n            outputFileName = abi == null ? \"barinsta_${suffix}.apk\" : \"barinsta_${suffix}_${abi}.apk\"\n        }\n    }\n\n    packagingOptions {\n        // Exclude file to avoid\n        // Error: Duplicate files during packaging of APK\n        exclude 'META-INF/LICENSE.md'\n        exclude 'META-INF/LICENSE-notice.md'\n        exclude 'META-INF/atomicfu.kotlin_module'\n        exclude 'META-INF/AL2.0'\n        exclude 'META-INF/LGPL2.1'\n    }\n\n    testOptions.unitTests {\n        includeAndroidResources = true\n    }\n\n}\n\nconfigurations.all {\n    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'\n}\n\ndependencies {\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'\n\n    def exoplayer_version = '2.14.1'\n\n    implementation 'com.google.android.material:material:1.4.0'\n\n    implementation \"com.google.android.exoplayer:exoplayer-core:$exoplayer_version\"\n    implementation \"com.google.android.exoplayer:exoplayer-dash:$exoplayer_version\"\n    implementation \"com.google.android.exoplayer:exoplayer-ui:$exoplayer_version\"\n\n    implementation \"androidx.recyclerview:recyclerview:1.2.1\"\n    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'\n    implementation \"androidx.viewpager2:viewpager2:1.0.0\"\n    implementation \"androidx.constraintlayout:constraintlayout:2.0.4\"\n    implementation \"androidx.preference:preference:1.1.1\"\n    implementation 'androidx.palette:palette:1.0.0'\n    implementation 'androidx.documentfile:documentfile:1.0.1'\n\n    implementation 'com.google.guava:guava:27.0.1-android'\n\n    def core_version = \"1.6.0\"\n    implementation \"androidx.core:core:$core_version\"\n\n    // Fragment\n    implementation \"androidx.fragment:fragment-ktx:1.3.5\"\n\n    // Lifecycle\n    implementation \"androidx.lifecycle:lifecycle-extensions:2.2.0\"\n    implementation \"androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1\"\n    implementation \"androidx.lifecycle:lifecycle-livedata-ktx:2.3.1\"\n\n    // Navigation\n    implementation \"androidx.navigation:navigation-fragment-ktx:$rootProject.nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$rootProject.nav_version\"\n\n    // Room\n    def room_version = \"2.3.0\"\n    implementation \"androidx.room:room-runtime:$room_version\"\n    implementation \"androidx.room:room-guava:$room_version\"\n    implementation \"androidx.room:room-ktx:$room_version\"\n    kapt \"androidx.room:room-compiler:$room_version\"\n    annotationProcessor \"androidx.room:room-compiler:$room_version\"\n\n    // CameraX\n    def camerax_version = \"1.1.0-alpha07\"\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n    implementation \"androidx.camera:camera-view:1.0.0-alpha27\"\n\n    // EmojiCompat\n    def emoji_compat_version = \"1.1.0\"\n    implementation \"androidx.emoji:emoji:$emoji_compat_version\"\n    implementation \"androidx.emoji:emoji-appcompat:$emoji_compat_version\"\n\n    // Work\n    def work_version = '2.5.0'\n    implementation \"androidx.work:work-runtime:$work_version\"\n    implementation \"androidx.work:work-runtime-ktx:$work_version\"\n\n    implementation \"ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0\"\n\n    implementation 'com.facebook.fresco:fresco:2.5.0'\n    implementation 'com.facebook.fresco:animated-webp:2.5.0'\n    implementation 'com.facebook.fresco:webpsupport:2.5.0'\n\n    implementation 'com.squareup.retrofit2:retrofit:2.9.0'\n    implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'\n    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'\n\n    implementation 'org.apache.commons:commons-imaging:1.0-alpha2'\n\n    implementation 'com.github.skydoves:balloon:1.3.5'\n\n    implementation 'com.github.ammargitham:AutoLinkTextViewV2:3.2.0'\n    implementation 'com.github.ammargitham:uCrop:2.3-non-native'\n    implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'\n\n    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'\n\n    githubImplementation 'io.sentry:sentry-android:5.0.1'\n\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'\n    testImplementation \"androidx.test.ext:junit-ktx:1.1.3\"\n    testImplementation \"androidx.test:core-ktx:1.4.0\"\n    testImplementation \"androidx.arch.core:core-testing:2.1.0\"\n    testImplementation \"org.robolectric:robolectric:4.5.1\"\n    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0'\n\n    androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'\n    androidTestImplementation 'androidx.test:core:1.4.0'\n    androidTestImplementation 'com.android.support:support-annotations:28.0.0'\n    androidTestImplementation 'com.android.support.test:runner:1.0.2'\n    androidTestImplementation \"androidx.room:room-testing:2.3.0\"\n    androidTestImplementation \"androidx.arch.core:core-testing:2.1.0\"\n    androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0'\n\n}\n"
  },
  {
    "path": "app/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n    <issue id=\"IconColors\">\n        <ignore path=\"src/main/res/drawable-night/download.png\" />\n    </issue>\n    <issue id=\"IconLocation\">\n        <ignore path=\"src/main/res/drawable/adm.png\" />\n        <ignore path=\"src/main/res/drawable/check.png\" />\n        <ignore path=\"src/main/res/drawable/collapse.png\" />\n        <ignore path=\"src/main/res/drawable/comments.png\" />\n        <ignore path=\"src/main/res/drawable/download.png\" />\n        <ignore path=\"src/main/res/drawable/expand.png\" />\n        <ignore path=\"src/main/res/drawable/lock.png\" />\n        <ignore path=\"src/main/res/drawable/lw.png\" />\n        <ignore path=\"src/main/res/drawable/ms.png\" />\n        <ignore path=\"src/main/res/drawable/mute.png\" />\n        <ignore path=\"src/main/res/drawable/qdb.png\" />\n        <ignore path=\"src/main/res/drawable/rev.png\" />\n        <ignore path=\"src/main/res/drawable/revl.png\" />\n        <ignore path=\"src/main/res/drawable/settings.png\" />\n        <ignore path=\"src/main/res/drawable/slider.png\" />\n        <ignore path=\"src/main/res/drawable/tesv.png\" />\n        <ignore path=\"src/main/res/drawable/vdz.png\" />\n        <ignore path=\"src/main/res/drawable/verified.png\" />\n        <ignore path=\"src/main/res/drawable/video.png\" />\n        <ignore path=\"src/main/res/drawable/video_views.png\" />\n        <ignore path=\"src/main/res/drawable/vol.png\" />\n    </issue>\n</lint>\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n\n#noinspection ShrinkerUnresolvedReference\n#-keep class !com.google.android.exoplayer2.**, ** { *; }\n\n-dontobfuscate\n\n# prevent shrinking retrofit response entities\n-keep class awais.instagrabber.repositories.responses.** { *; }"
  },
  {
    "path": "app/schemas/awais.instagrabber.db.AppDatabase/4.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 4,\n    \"identityHash\": \"538d64adaeb8c3a98db9204955932e59\",\n    \"entities\": [\n      {\n        \"tableName\": \"accounts\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"username\",\n            \"columnName\": \"username\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cookie\",\n            \"columnName\": \"cookie\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"fullName\",\n            \"columnName\": \"full_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"profilePic\",\n            \"columnName\": \"profile_pic\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"favorites\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"query\",\n            \"columnName\": \"query_text\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"displayName\",\n            \"columnName\": \"display_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"picUrl\",\n            \"columnName\": \"pic_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"dateAdded\",\n            \"columnName\": \"date_added\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '538d64adaeb8c3a98db9204955932e59')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/schemas/awais.instagrabber.db.AppDatabase/5.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 5,\n    \"identityHash\": \"0b38e12b76bb081ec837191c5ef5b54e\",\n    \"entities\": [\n      {\n        \"tableName\": \"accounts\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"username\",\n            \"columnName\": \"username\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cookie\",\n            \"columnName\": \"cookie\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"fullName\",\n            \"columnName\": \"full_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"profilePic\",\n            \"columnName\": \"profile_pic\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"favorites\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"query\",\n            \"columnName\": \"query_text\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"displayName\",\n            \"columnName\": \"display_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"picUrl\",\n            \"columnName\": \"pic_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"dateAdded\",\n            \"columnName\": \"date_added\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"dm_last_notified\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `thread_id` TEXT, `last_notified_msg_ts` INTEGER, `last_notified_at` INTEGER)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"threadId\",\n            \"columnName\": \"thread_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"lastNotifiedMsgTs\",\n            \"columnName\": \"last_notified_msg_ts\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"lastNotifiedAt\",\n            \"columnName\": \"last_notified_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [\n          {\n            \"name\": \"index_dm_last_notified_thread_id\",\n            \"unique\": true,\n            \"columnNames\": [\n              \"thread_id\"\n            ],\n            \"createSql\": \"CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `${TABLE_NAME}` (`thread_id`)\"\n          }\n        ],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0b38e12b76bb081ec837191c5ef5b54e')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/schemas/awais.instagrabber.db.AppDatabase/6.json",
    "content": "{\n  \"formatVersion\": 1,\n  \"database\": {\n    \"version\": 6,\n    \"identityHash\": \"232e618b3bfcb4661336b359d036c455\",\n    \"entities\": [\n      {\n        \"tableName\": \"accounts\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"uid\",\n            \"columnName\": \"uid\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"username\",\n            \"columnName\": \"username\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"cookie\",\n            \"columnName\": \"cookie\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"fullName\",\n            \"columnName\": \"full_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"profilePic\",\n            \"columnName\": \"profile_pic\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"favorites\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"query\",\n            \"columnName\": \"query_text\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"displayName\",\n            \"columnName\": \"display_name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"picUrl\",\n            \"columnName\": \"pic_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"dateAdded\",\n            \"columnName\": \"date_added\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"dm_last_notified\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `thread_id` TEXT, `last_notified_msg_ts` INTEGER, `last_notified_at` INTEGER)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"threadId\",\n            \"columnName\": \"thread_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"lastNotifiedMsgTs\",\n            \"columnName\": \"last_notified_msg_ts\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"lastNotifiedAt\",\n            \"columnName\": \"last_notified_at\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": false\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [\n          {\n            \"name\": \"index_dm_last_notified_thread_id\",\n            \"unique\": true,\n            \"columnNames\": [\n              \"thread_id\"\n            ],\n            \"createSql\": \"CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `${TABLE_NAME}` (`thread_id`)\"\n          }\n        ],\n        \"foreignKeys\": []\n      },\n      {\n        \"tableName\": \"recent_searches\",\n        \"createSql\": \"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `ig_id` TEXT NOT NULL, `name` TEXT NOT NULL, `username` TEXT, `pic_url` TEXT, `type` TEXT NOT NULL, `last_searched_on` INTEGER NOT NULL)\",\n        \"fields\": [\n          {\n            \"fieldPath\": \"id\",\n            \"columnName\": \"id\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"igId\",\n            \"columnName\": \"ig_id\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"name\",\n            \"columnName\": \"name\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"username\",\n            \"columnName\": \"username\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"picUrl\",\n            \"columnName\": \"pic_url\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": false\n          },\n          {\n            \"fieldPath\": \"type\",\n            \"columnName\": \"type\",\n            \"affinity\": \"TEXT\",\n            \"notNull\": true\n          },\n          {\n            \"fieldPath\": \"lastSearchedOn\",\n            \"columnName\": \"last_searched_on\",\n            \"affinity\": \"INTEGER\",\n            \"notNull\": true\n          }\n        ],\n        \"primaryKey\": {\n          \"columnNames\": [\n            \"id\"\n          ],\n          \"autoGenerate\": true\n        },\n        \"indices\": [\n          {\n            \"name\": \"index_recent_searches_ig_id_type\",\n            \"unique\": true,\n            \"columnNames\": [\n              \"ig_id\",\n              \"type\"\n            ],\n            \"createSql\": \"CREATE UNIQUE INDEX IF NOT EXISTS `index_recent_searches_ig_id_type` ON `${TABLE_NAME}` (`ig_id`, `type`)\"\n          }\n        ],\n        \"foreignKeys\": []\n      }\n    ],\n    \"views\": [],\n    \"setupQueries\": [\n      \"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)\",\n      \"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '232e618b3bfcb4661336b359d036c455')\"\n    ]\n  }\n}"
  },
  {
    "path": "app/sentry.gradle",
    "content": "def dsnKey = 'DSN'\ndef defaultDsn = '\\\"\\\"'\n\nfinal Properties properties = new Properties()\nFile propertiesFile = rootProject.file('sentry.properties')\nif (!propertiesFile.exists()) {\n    propertiesFile.createNewFile()\n}\nproperties.load(new FileInputStream(propertiesFile))\n\next{\n    SENTRY_DSN = properties.getProperty(dsnKey, defaultDsn)\n}"
  },
  {
    "path": "app/src/androidTest/java/awais/instagrabber/db/MigrationTest.java",
    "content": "package awais.instagrabber.db;\n\nimport androidx.room.Room;\nimport androidx.room.migration.Migration;\nimport androidx.room.testing.MigrationTestHelper;\nimport androidx.sqlite.db.SupportSQLiteDatabase;\nimport androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.runner.AndroidJUnit4;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.io.IOException;\n\nimport static awais.instagrabber.db.AppDatabase.MIGRATION_4_5;\nimport static awais.instagrabber.db.AppDatabase.MIGRATION_5_6;\n\n@RunWith(AndroidJUnit4.class)\npublic class MigrationTest {\n    private static final String TEST_DB = \"migration-test\";\n    private static final Migration[] ALL_MIGRATIONS = new Migration[]{MIGRATION_4_5, MIGRATION_5_6};\n\n    @Rule\n    public MigrationTestHelper helper;\n\n    public MigrationTest() {\n        final String canonicalName = AppDatabase.class.getCanonicalName();\n        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),\n                                         canonicalName,\n                                         new FrameworkSQLiteOpenHelperFactory());\n    }\n\n    @Test\n    public void migrateAll() throws IOException {\n        // Create earliest version of the database. Have to start with 4 since that is the version we migrated to Room.\n        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 4);\n        db.close();\n\n        // Open latest version of the database. Room will validate the schema\n        // once all migrations execute.\n        AppDatabase appDb = Room.databaseBuilder(InstrumentationRegistry.getInstrumentation().getTargetContext(),\n                                                 AppDatabase.class,\n                                                 TEST_DB)\n                                .addMigrations(ALL_MIGRATIONS).build();\n        appDb.getOpenHelper().getWritableDatabase();\n        appDb.close();\n    }\n}\n"
  },
  {
    "path": "app/src/androidTest/java/awais/instagrabber/db/dao/RecentSearchDaoTest.kt",
    "content": "package awais.instagrabber.db.dao\n\nimport android.content.Context\nimport androidx.arch.core.executor.testing.InstantTaskExecutorRule\nimport androidx.room.Room\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.runner.AndroidJUnit4\nimport awais.instagrabber.db.AppDatabase\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.models.enums.FavoriteType\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.test.runBlockingTest\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.jupiter.api.Assertions\nimport org.junit.runner.RunWith\nimport java.time.LocalDateTime\n\n@RunWith(AndroidJUnit4::class)\nclass RecentSearchDaoTest {\n    private lateinit var db: AppDatabase\n    private lateinit var dao: RecentSearchDao\n\n    @get:Rule\n    var instantExecutorRule = InstantTaskExecutorRule()\n\n    @Before\n    fun createDb() {\n        val context = ApplicationProvider.getApplicationContext<Context>()\n        db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()\n        dao = db.recentSearchDao()\n    }\n\n    @After\n    fun closeDb() {\n        db.close()\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun writeQueryDelete() = runBlockingTest {\n        val recentSearch = insertRecentSearch(1, \"1\", \"test1\", FavoriteType.HASHTAG)\n        val byIgIdAndType = dao.getRecentSearchByIgIdAndType(\"1\", FavoriteType.HASHTAG)\n        Assertions.assertNotNull(byIgIdAndType)\n        Assertions.assertEquals(recentSearch, byIgIdAndType)\n        dao.deleteRecentSearch(byIgIdAndType ?: throw NullPointerException())\n        val deleted = dao.getRecentSearchByIgIdAndType(\"1\", FavoriteType.HASHTAG)\n        Assertions.assertNull(deleted)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun queryAllOrdered() = runBlockingTest {\n        val insertListReversed: List<RecentSearch> = listOf(\n            insertRecentSearch(1, \"1\", \"test1\", FavoriteType.HASHTAG),\n            insertRecentSearch(2, \"2\", \"test2\", FavoriteType.LOCATION),\n            insertRecentSearch(3, \"3\", \"test3\", FavoriteType.USER),\n            insertRecentSearch(4, \"4\", \"test4\", FavoriteType.USER),\n            insertRecentSearch(5, \"5\", \"test5\", FavoriteType.USER)\n        ).asReversed() // important\n        val fromDb: List<RecentSearch?> = dao.getAllRecentSearches()\n        Assertions.assertIterableEquals(insertListReversed, fromDb)\n    }\n\n    private fun insertRecentSearch(id: Int, igId: String, name: String, type: FavoriteType): RecentSearch {\n        val recentSearch = RecentSearch(\n            id,\n            igId,\n            name,\n            null,\n            null,\n            type,\n            LocalDateTime.now()\n        )\n        runBlocking { dao.insertRecentSearch(recentSearch) }\n        return recentSearch\n    }\n}"
  },
  {
    "path": "app/src/androidTest/java/awaisomereport/CrashReporterHelperTest.kt",
    "content": "package awaisomereport\n\nimport androidx.test.runner.AndroidJUnit4\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@RunWith(AndroidJUnit4::class)\ninternal class CrashReporterHelperTest {\n\n    @Test\n    fun getErrorContent() {\n        val errorContent = CrashReporterHelper.getReportContent(Exception())\n        print(errorContent)\n    }\n}"
  },
  {
    "path": "app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.preference.Preference;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.fragments.settings.IFlavorSettings;\nimport awais.instagrabber.fragments.settings.SettingCategory;\n\npublic final class FlavorSettings implements IFlavorSettings {\n\n    private static FlavorSettings instance;\n\n    private FlavorSettings() {\n    }\n\n    public static FlavorSettings getInstance() {\n        if (instance == null) {\n            instance = new FlavorSettings();\n        }\n        return instance;\n    }\n\n    @NonNull\n    @Override\n    public List<Preference> getPreferences(@NonNull final Context context,\n                                           @NonNull final FragmentManager fragmentManager,\n                                           @NonNull final SettingCategory settingCategory) {\n        // switch (settingCategory) {\n        //     default:\n        //         break;\n        // }\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "app/src/fdroid/java/awais/instagrabber/utils/UpdateChecker.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport org.json.JSONObject;\n\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\npublic class UpdateChecker {\n    private static final Object LOCK = new Object();\n    private static final String TAG = UpdateChecker.class.getSimpleName();\n\n    private static UpdateChecker instance;\n\n    public static UpdateChecker getInstance() {\n        if (instance == null) {\n            synchronized (LOCK) {\n                if (instance == null) {\n                    instance = new UpdateChecker();\n                }\n            }\n        }\n        return instance;\n    }\n\n    /**\n     * Needs to be called asynchronously\n     *\n     * @return the latest version from f-droid\n     */\n    @Nullable\n    public String getLatestVersion() {\n        HttpURLConnection conn = null;\n        try {\n            conn = (HttpURLConnection) new URL(\"https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber\").openConnection();\n            conn.setUseCaches(false);\n            conn.setRequestProperty(\"User-Agent\", \"https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me\");\n            conn.connect();\n            final int responseCode = conn.getResponseCode();\n            if (responseCode == HttpURLConnection.HTTP_OK) {\n                final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));\n                return \"v\" + data.getJSONArray(\"packages\").getJSONObject(0).getString(\"versionName\");\n                // if (BuildConfig.VERSION_CODE < data.getInt(\"suggestedVersionCode\")) {\n                // }\n            }\n        } catch (final Exception e) {\n            Log.e(TAG, \"\", e);\n        } finally {\n            if (conn != null) {\n                conn.disconnect();\n            }\n        }\n        return null;\n    }\n\n    public void onDownload(@NonNull final AppCompatActivity context) {\n        Utils.openURL(context, \"https://f-droid.org/packages/me.austinhuang.instagrabber/\");\n    }\n}\n"
  },
  {
    "path": "app/src/fdroid/java/awaisomereport/CrashHandler.kt",
    "content": "package awaisomereport\n\nimport android.app.Application\n\nclass CrashHandler(private val application: Application) : ICrashHandler {\n    override fun uncaughtException(\n        t: Thread,\n        exception: Throwable,\n        defaultExceptionHandler: Thread.UncaughtExceptionHandler\n    ) {\n        CrashReporterHelper.startErrorReporterActivity(application, exception)\n        defaultExceptionHandler.uncaughtException(t, exception)\n    }\n}"
  },
  {
    "path": "app/src/github/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"awais.instagrabber\">\n\n    <application>\n        <meta-data\n            android:name=\"io.sentry.auto-init\"\n            android:value=\"false\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.preference.Preference;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.ConfirmDialogFragment;\n\nimport static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic final class FlavorSettings implements IFlavorSettings {\n\n    private static FlavorSettings instance;\n\n    private FlavorSettings() {\n    }\n\n    public static FlavorSettings getInstance() {\n        if (instance == null) {\n            instance = new FlavorSettings();\n        }\n        return instance;\n    }\n\n    @NonNull\n    @Override\n    public List<Preference> getPreferences(@NonNull final Context context,\n                                           @NonNull final FragmentManager fragmentManager,\n                                           @NonNull final SettingCategory settingCategory) {\n        switch (settingCategory) {\n            case GENERAL:\n                return getGeneralPrefs(context, fragmentManager);\n            default:\n                break;\n        }\n        return Collections.emptyList();\n    }\n\n    private List<Preference> getGeneralPrefs(@NonNull final Context context,\n                                             @NonNull final FragmentManager fragmentManager) {\n        return ImmutableList.of(\n                getSentryPreference(context, fragmentManager)\n        );\n    }\n\n    private Preference getSentryPreference(@NonNull final Context context,\n                                           @NonNull final FragmentManager fragmentManager) {\n        if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) {\n            // disabled by default\n            settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false);\n        }\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PREF_ENABLE_SENTRY,\n                R.string.enable_sentry,\n                R.string.sentry_summary,\n                false,\n                (preference, newValue) -> {\n                    if (!(newValue instanceof Boolean)) return true;\n                    final boolean enabled = (Boolean) newValue;\n                    if (enabled) {\n                        final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(\n                                111,\n                                0,\n                                R.string.sentry_start_next_launch,\n                                R.string.ok,\n                                0,\n                                0);\n                        dialogFragment.show(fragmentManager, \"sentry_dialog\");\n                    }\n                    return true;\n                });\n    }\n}\n"
  },
  {
    "path": "app/src/github/java/awais/instagrabber/utils/UpdateChecker.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\npublic class UpdateChecker {\n    private static final Object LOCK = new Object();\n    private static final String TAG = UpdateChecker.class.getSimpleName();\n\n    private static UpdateChecker instance;\n\n    public static UpdateChecker getInstance() {\n        if (instance == null) {\n            synchronized (LOCK) {\n                if (instance == null) {\n                    instance = new UpdateChecker();\n                }\n            }\n        }\n        return instance;\n    }\n\n    /**\n     * Needs to be called asynchronously\n     *\n     * @return the latest version from Github\n     */\n    @Nullable\n    public String getLatestVersion() {\n        HttpURLConnection conn = null;\n        try {\n            conn = (HttpURLConnection) new URL(\"https://github.com/austinhuang0131/barinsta/releases/latest\").openConnection();\n            conn.setInstanceFollowRedirects(false);\n            conn.setUseCaches(false);\n            conn.setRequestProperty(\"User-Agent\", \"https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me\");\n            conn.connect();\n            final int responseCode = conn.getResponseCode();\n            if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {\n                return \"v\" + conn.getHeaderField(\"Location\").split(\"/v\")[1];\n                // return !version.equals(BuildConfig.VERSION_NAME);\n            }\n        } catch (final Exception e) {\n            Log.e(TAG, \"\", e);\n        } finally {\n            if (conn != null) {\n                conn.disconnect();\n            }\n        }\n        return null;\n    }\n\n    public void onDownload(@NonNull final Context context) {\n        Utils.openURL(context, \"https://github.com/austinhuang0131/instagrabber/releases/latest\");\n    }\n}\n"
  },
  {
    "path": "app/src/github/java/awaisomereport/CrashHandler.kt",
    "content": "package awaisomereport\n\nimport android.app.Application\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.utils.Utils\nimport io.sentry.SentryEvent\nimport io.sentry.SentryLevel\nimport io.sentry.SentryOptions.BeforeSendCallback\nimport io.sentry.android.core.SentryAndroid\nimport io.sentry.android.core.SentryAndroidOptions\n\nclass CrashHandler(private val application: Application) : ICrashHandler {\n    private var enabled = false\n\n    init {\n        enabled = if (!Utils.settingsHelper.hasPreference(PreferenceKeys.PREF_ENABLE_SENTRY)) {\n            // disabled by default (change to true if we need enabled by default)\n            false\n        } else {\n            Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_SENTRY)\n        }\n        if (enabled) {\n            SentryAndroid.init(application) { options: SentryAndroidOptions ->\n                options.dsn = BuildConfig.dsn\n                options.setDiagnosticLevel(SentryLevel.ERROR)\n                options.beforeSend = BeforeSendCallback { event: SentryEvent, _: Any? ->\n                    // Removing unneeded info from event\n                    event.contexts.device?.apply {\n                        name = null\n                        timezone = null\n                        isCharging = null\n                        bootTime = null\n                        freeStorage = null\n                        batteryTemperature = null\n                    }\n                    event\n                }\n            }\n        }\n    }\n\n    override fun uncaughtException(\n        t: Thread,\n        exception: Throwable,\n        defaultExceptionHandler: Thread.UncaughtExceptionHandler\n    ) {\n        // When enabled, Sentry auto captures unhandled exceptions\n        if (!enabled) {\n            CrashReporterHelper.startErrorReporterActivity(application, exception)\n        }\n        defaultExceptionHandler.uncaughtException(t, exception)\n    }\n}"
  },
  {
    "path": "app/src/github/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>"
  },
  {
    "path": "app/src/github/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">تمكين الحراسة</string>\n    <string name=\"sentry_summary\">الحراسة هي مستمع/معالج للأخطاء الذي يرسل الخطأ/الاحداث إلى Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">ستبدأ الحراسة عند التشغيل التالي</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Habilita el Sentry</string>\n    <string name=\"sentry_summary\">Sentry és un oient/intèrpret d\\'error que envia asíncronament l\\'error/esdeveniment a Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry s\\'iniciarà al pròxim llançament</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Povolit Sentry</string>\n    <string name=\"sentry_summary\">Sentry je listener/handler, který zaznamenává chyby a asynchronně je posílá na Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry se spustí při příštím spuštění</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Aktiviere Sentry</string>\n    <string name=\"sentry_summary\">Sentry ist ein Listener/Handler für Fehler, der den Fehler/das Ereignis asynchron an Sentry.io sendet</string>\n    <string name=\"sentry_start_next_launch\">Sentry startet beim nächsten Start</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Ενεργοποίηση Sentry</string>\n    <string name=\"sentry_summary\">Το Sentry είναι διαχειριστής σφαλμάτων ασύγχρονης αποστολής του σφάλματος/συμβάντος στο Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Το Sentry θα ξεκινήσει στην επόμενη εκκίνηση</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Activar Sentry</string>\n    <string name=\"sentry_summary\">Sentry es un oyente/manejador de errores que asincrónicamente envía el error/evento a Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry comenzará en el próximo inicio</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-eu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">فعالسازی Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry در اجرای بعدی، شروع خواهد شد</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Activer Sentry</string>\n    <string name=\"sentry_summary\">Sentry est un écouteur/gestionnaire d\\'erreurs qui envoie de manière asynchrone l\\'erreur/l\\'événement à Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry commencera au prochain lancement</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-hi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Hidupkan Sentry</string>\n    <string name=\"sentry_summary\">Sentry adalah sebuah pendengar/penanganan eror yang secara asinkronis mengirimkan eror/kejadian ke Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry akan dihidupkan pada peluncuran berikutnya</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Abilita Sentry</string>\n    <string name=\"sentry_summary\">Sentry è un ascoltatore/gestore di errori che invia asincronicamente l\\'errore/evento a Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry comincerà al prossimo lancio</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Sentry 활성화</string>\n    <string name=\"sentry_summary\">Sentry는 Sentry.io에 오류를 비동기적으로 보내는 오류 처리기입니다</string>\n    <string name=\"sentry_start_next_launch\">Sentry는 다음 출시에 시작됩니다</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-mk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Овозможи Sentry</string>\n    <string name=\"sentry_summary\">Sentry е слушач на грешки кој асинхроно ги испраќа на Sentry.io страната</string>\n    <string name=\"sentry_start_next_launch\">Sentry ќе биде овозможен на следно отварање</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Sentry inschakelen</string>\n    <string name=\"sentry_summary\">Sentry is een luister/handler voor fouten die asynchroon de fout/gebeurtenis versturen naar Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry zal starten bij de volgende lancering</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-or/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Sentryକୁ ସକ୍ଷମ କରନ୍ତୁ</string>\n    <string name=\"sentry_summary\">ତ୍ରୁଟି ପାଇଁ ସେଣ୍ଟ୍ରି ହେଉଛି ଏକ ଶ୍ରୋତା ଯାହା ତ୍ରୁଟି / ଘଟଣାକୁ Sentry.io କୁ ପଠାଏ |</string>\n    <string name=\"sentry_start_next_launch\">ପରବର୍ତ୍ତୀ ଲଞ୍ଚଠାରୁ ସେଣ୍ଟ୍ରି ଆରମ୍ଭ ହେବ |</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Włącz Sentry</string>\n    <string name=\"sentry_summary\">Sentry jest słuchaczem/obsługą błędów, które asynchronicznie wysyłają błąd/zdarzenie do Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry rozpocznie się przy następnym uruchomieniu</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Ativar Sentry</string>\n    <string name=\"sentry_summary\">Sentry é um ouvinte/gestor de erros que assincronicamente envia o erro/evento para Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry começará no próximo início</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Включить Sentry</string>\n    <string name=\"sentry_summary\">Sentry - это обработчик событий, который асинхронно отправляет сообщения об ошибках/поломках на Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry включится при следующем запуске приложения</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Enable Sentry</string>\n    <string name=\"sentry_summary\">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">Sentry will start on next launch</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">Bật Sentry</string>\n    <string name=\"sentry_summary\">Sentry là một thiết bị nghe/giải quyết cho những lỗi mà gửi những lỗi/sự kiện đến Sentry.io một cách tách biệt</string>\n    <string name=\"sentry_start_next_launch\">Sentry sẽ được bật vào lần khởi động kế tiếp</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">启用 Sentry</string>\n    <string name=\"sentry_summary\">Sentry 会将错误报告发送至 Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">启用 Sentry 将在下次启动应用时生效</string>\n</resources>\n"
  },
  {
    "path": "app/src/github/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"enable_sentry\">啟用 Sentry</string>\n    <string name=\"sentry_summary\">Sentry 會將錯誤報告發送至 Sentry.io</string>\n    <string name=\"sentry_start_next_launch\">下次啟用應用程式時將會開啟 Sentry</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"awais.instagrabber\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <!--<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />-->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <!--<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />-->\n\n    <uses-feature\n        android:name=\"android.hardware.camera.any\"\n        android:required=\"false\" />\n\n    <application\n        android:name=\".InstaGrabberApplication\"\n        android:allowBackup=\"true\"\n        android:backupAgent=\".backup.BarinstaBackupAgent\"\n        android:fullBackupContent=\"@xml/backup_descriptor\"\n        android:fullBackupOnly=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme.Launcher\"\n        tools:ignore=\"UnusedAttribute\">\n        <activity\n            android:name=\".activities.MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:taskAffinity=\".Main\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.SEND\" />\n                <!-- <action android:name=\"android.intent.action.SEARCH\" /> -->\n                <!-- <action android:name=\"android.intent.action.WEB_SEARCH\" /> -->\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:mimeType=\"text/plain\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:host=\"instagr.am\" />\n                <data android:host=\"www.instagr.am\" />\n                <data android:host=\"instagram.com\" />\n                <data android:host=\"www.instagram.com\" />\n                <data android:pathPrefix=\"/\" />\n                <data android:pathPrefix=\"/p\" />\n                <data android:pathPrefix=\"/explore/tags\" />\n                <data android:pathPrefix=\"/explore/locations\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"awaisomereport.ErrorReporterActivity\"\n            android:allowEmbedded=\"false\"\n            android:allowTaskReparenting=\"false\"\n            android:autoRemoveFromRecents=\"true\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation|screenSize\"\n            android:documentLaunchMode=\"never\"\n            android:excludeFromRecents=\"true\"\n            android:finishOnTaskLaunch=\"true\"\n            android:launchMode=\"singleTask\"\n            android:lockTaskMode=\"never\"\n            android:noHistory=\"false\"\n            android:screenOrientation=\"portrait\"\n            android:taskAffinity=\"awais.instagrabber.errorreport\"\n            android:theme=\"@android:style/Theme.DeviceDefault.Dialog\" />\n        <activity\n            android:name=\".activities.Login\"\n            android:label=\"@string/login\"\n            android:parentActivityName=\".activities.MainActivity\"\n            android:theme=\"@style/AppTheme.Light.White\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".activities.MainActivity\" />\n        </activity>\n        <activity\n            android:name=\".activities.CameraActivity\"\n            android:label=\"Camera\"\n            android:parentActivityName=\".activities.MainActivity\"\n            android:theme=\"@style/AppTheme.Light.White\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".activities.MainActivity\" />\n        </activity>\n        <activity\n            android:name=\".utils.ProcessPhoenix\"\n            android:theme=\"@style/Theme.AppCompat.Translucent\" />\n        <activity android:name=\".activities.DirectorySelectActivity\" />\n\n        <provider\n            android:name=\"androidx.core.content.FileProvider\"\n            android:authorities=\"${applicationId}.provider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/provider_paths\" />\n        </provider>\n\n        <service\n            android:name=\".services.ActivityCheckerService\"\n            android:exported=\"false\" />\n        <service\n            android:name=\".services.DeleteImageIntentService\"\n            android:exported=\"false\" />\n        <service\n            android:name=\".services.DMSyncService\"\n            android:exported=\"false\" />\n\n        <receiver\n            android:name=\".services.DMSyncAlarmReceiver\"\n            android:enabled=\"true\"\n            android:exported=\"false\" />\n        <!--<receiver android:name=\".services.BootCompletedReceiver\" >-->\n        <!--    <intent-filter>-->\n        <!--        <action android:name=\"android.intent.action.BOOT_COMPLETED\" />-->\n        <!--    </intent-filter>-->\n        <!--</receiver>-->\n\n        <uses-library\n            android:name=\"org.apache.http.legacy\"\n            android:required=\"false\" />\n\n        <meta-data\n            android:name=\"fontProviderRequests\"\n            android:value=\"Noto Color Emoji Compat\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/InstaGrabberApplication.kt",
    "content": "package awais.instagrabber\n\nimport android.app.Application\nimport android.content.ClipboardManager\nimport android.util.Log\nimport awais.instagrabber.fragments.settings.PreferenceKeys.CUSTOM_DATE_TIME_FORMAT\nimport awais.instagrabber.fragments.settings.PreferenceKeys.CUSTOM_DATE_TIME_FORMAT_ENABLED\nimport awais.instagrabber.fragments.settings.PreferenceKeys.DATE_TIME_FORMAT\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.LocaleUtils.currentLocale\nimport awais.instagrabber.utils.Utils.settingsHelper\nimport awais.instagrabber.utils.extensions.TAG\nimport awaisomereport.CrashReporter\nimport com.facebook.drawee.backends.pipeline.Fresco\nimport com.facebook.imagepipeline.core.ImagePipelineConfig\nimport java.net.CookieHandler\nimport java.time.format.DateTimeFormatter\nimport java.util.*\n\n@Suppress(\"unused\")\nclass InstaGrabberApplication : Application() {\n    override fun onCreate() {\n        super.onCreate()\n        CookieHandler.setDefault(NET_COOKIE_MANAGER)\n        settingsHelper = SettingsHelper(this)\n        setupCrashReporter()\n        setupCloseGuard()\n        setupFresco()\n        Utils.cacheDir = cacheDir.absolutePath\n        Utils.clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager\n        LocaleUtils.setLocale(baseContext)\n        val pattern = if (settingsHelper.getBoolean(CUSTOM_DATE_TIME_FORMAT_ENABLED)) {\n            settingsHelper.getString(CUSTOM_DATE_TIME_FORMAT)\n        } else {\n            settingsHelper.getString(DATE_TIME_FORMAT)\n        }\n        TextUtils.setFormatter(DateTimeFormatter.ofPattern(pattern, currentLocale))\n        if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) {\n            settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString())\n        }\n    }\n\n    private fun setupCrashReporter() {\n        if (BuildConfig.DEBUG) return\n        CrashReporter.getInstance(this).start()\n    }\n\n    private fun setupCloseGuard() {\n        if (!BuildConfig.DEBUG) return\n        try {\n            Class.forName(\"dalvik.system.CloseGuard\")\n                .getMethod(\"setEnabled\", Boolean::class.javaPrimitiveType)\n                .invoke(null, true)\n        } catch (e: Exception) {\n            Log.e(TAG, \"Error\", e)\n        }\n    }\n\n    private fun setupFresco() {\n        // final Set<RequestListener> requestListeners = new HashSet<>();\n        // requestListeners.add(new RequestLoggingListener());\n        val imagePipelineConfig = ImagePipelineConfig\n            .newBuilder(this) // .setMainDiskCacheConfig(diskCacheConfig)\n            // .setRequestListeners(requestListeners)\n            .setDownsampleEnabled(true)\n            .build()\n        Fresco.initialize(this, imagePipelineConfig)\n        // FLog.setMinimumLoggingLevel(FLog.VERBOSE);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/activities/BaseLanguageActivity.kt",
    "content": "package awais.instagrabber.activities\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport awais.instagrabber.utils.LocaleUtils\nimport awais.instagrabber.utils.ThemeUtils\n\nabstract class BaseLanguageActivity protected constructor() : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        ThemeUtils.changeTheme(this)\n        super.onCreate(savedInstanceState)\n    }\n\n    init {\n        @Suppress(\"LeakingThis\")\n        LocaleUtils.updateConfig(this)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/activities/CameraActivity.kt",
    "content": "package awais.instagrabber.activities\n\nimport android.content.Intent\nimport android.content.res.Configuration\nimport android.hardware.display.DisplayManager\nimport android.hardware.display.DisplayManager.DisplayListener\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport androidx.camera.core.*\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.documentfile.provider.DocumentFile\nimport awais.instagrabber.databinding.ActivityCameraBinding\nimport awais.instagrabber.utils.DownloadUtils\nimport awais.instagrabber.utils.PermissionUtils\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport java.io.IOException\nimport java.text.SimpleDateFormat\nimport java.util.*\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass CameraActivity : BaseLanguageActivity() {\n    private lateinit var binding: ActivityCameraBinding\n    private lateinit var displayManager: DisplayManager\n    private lateinit var cameraExecutor: ExecutorService\n\n    private var outputDirectory: DocumentFile? = null\n    private var imageCapture: ImageCapture? = null\n    private var displayId = -1\n    private var cameraProvider: ProcessCameraProvider? = null\n    private var lensFacing = 0\n\n    private val cameraRequestCode = 100\n    private val simpleDateFormat = SimpleDateFormat(\"yyyy-MM-dd-HH-mm-ss-SSS\", Locale.US)\n    private val displayListener: DisplayListener = object : DisplayListener {\n        override fun onDisplayAdded(displayId: Int) {}\n        override fun onDisplayRemoved(displayId: Int) {}\n        override fun onDisplayChanged(displayId: Int) {\n            if (displayId == this@CameraActivity.displayId) {\n                imageCapture?.targetRotation = binding.root.display.rotation\n            }\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityCameraBinding.inflate(LayoutInflater.from(baseContext))\n        setContentView(binding.root)\n        Utils.transparentStatusBar(this, true, false)\n        displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager\n        outputDirectory = DownloadUtils.cameraDir\n        cameraExecutor = Executors.newSingleThreadExecutor()\n        displayManager.registerDisplayListener(displayListener, null)\n        binding.viewFinder.post {\n            displayId = binding.viewFinder.display.displayId\n            updateUi()\n            checkPermissionsAndSetupCamera()\n        }\n    }\n\n    override fun onResume() {\n        super.onResume()\n        // Make sure that all permissions are still present, since the\n        // user could have removed them while the app was in paused state.\n        if (!PermissionUtils.hasCameraPerms(this)) {\n            PermissionUtils.requestCameraPerms(this, cameraRequestCode)\n        }\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        // Redraw the camera UI controls\n        updateUi()\n\n        // Enable or disable switching between cameras\n        updateCameraSwitchButton()\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        Utils.transparentStatusBar(this, false, false)\n        cameraExecutor.shutdown()\n        displayManager.unregisterDisplayListener(displayListener)\n    }\n\n    private fun updateUi() {\n        binding.cameraCaptureButton.setOnClickListener { takePhoto() }\n        // Disable the button until the camera is set up\n        binding.switchCamera.isEnabled = false\n        // Listener for button used to switch cameras. Only called if the button is enabled\n        binding.switchCamera.setOnClickListener {\n            lensFacing = if (CameraSelector.LENS_FACING_FRONT == lensFacing) CameraSelector.LENS_FACING_BACK else CameraSelector.LENS_FACING_FRONT\n            // Re-bind use cases to update selected camera\n            bindCameraUseCases()\n        }\n        binding.close.setOnClickListener {\n            setResult(RESULT_CANCELED)\n            finish()\n        }\n    }\n\n    private fun checkPermissionsAndSetupCamera() {\n        if (PermissionUtils.hasCameraPerms(this)) {\n            setupCamera()\n            return\n        }\n        PermissionUtils.requestCameraPerms(this, cameraRequestCode)\n    }\n\n    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n        if (requestCode == cameraRequestCode) {\n            if (PermissionUtils.hasCameraPerms(this)) {\n                setupCamera()\n            }\n        }\n    }\n\n    private fun setupCamera() {\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)\n        cameraProviderFuture.addListener({\n            try {\n                cameraProvider = cameraProviderFuture.get()\n                // Select lensFacing depending on the available cameras\n                lensFacing = -1\n                if (hasBackCamera()) {\n                    lensFacing = CameraSelector.LENS_FACING_BACK\n                } else if (hasFrontCamera()) {\n                    lensFacing = CameraSelector.LENS_FACING_FRONT\n                }\n                check(lensFacing != -1) { \"Back and front camera are unavailable\" }\n                // Enable or disable switching between cameras\n                updateCameraSwitchButton()\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            } catch (e: ExecutionException) {\n                Log.e(TAG, \"setupCamera: \", e)\n            } catch (e: InterruptedException) {\n                Log.e(TAG, \"setupCamera: \", e)\n            } catch (e: CameraInfoUnavailableException) {\n                Log.e(TAG, \"setupCamera: \", e)\n            }\n        }, ContextCompat.getMainExecutor(this))\n    }\n\n    private fun bindCameraUseCases() {\n        val rotation = binding.viewFinder.display.rotation\n\n        // CameraSelector\n        val cameraSelector = CameraSelector.Builder()\n            .requireLensFacing(lensFacing)\n            .build()\n\n        // Preview\n        val preview = Preview.Builder() // Set initial target rotation\n            .setTargetRotation(rotation)\n            .build()\n\n        // ImageCapture\n        imageCapture = ImageCapture.Builder()\n            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) // Set initial target rotation, we will have to call this again if rotation changes\n            // during the lifecycle of this use case\n            .setTargetRotation(rotation)\n            .build()\n        cameraProvider?.unbindAll()\n        cameraProvider?.bindToLifecycle(this, cameraSelector, preview, imageCapture)\n        preview.setSurfaceProvider(binding.viewFinder.surfaceProvider)\n    }\n\n    private fun takePhoto() {\n        if (imageCapture == null) return\n        val fileName = simpleDateFormat.format(System.currentTimeMillis()) + \".jpg\"\n        val mimeType = \"image/jpg\"\n        val photoFile = outputDirectory?.createFile(mimeType, fileName)?.let { it } ?: return\n        val outputStream = contentResolver.openOutputStream(photoFile.uri)?.let { it } ?: return\n        val outputFileOptions = ImageCapture.OutputFileOptions.Builder(outputStream).build()\n        imageCapture?.takePicture(\n            outputFileOptions,\n            cameraExecutor,\n            object : ImageCapture.OnImageSavedCallback {\n                @Suppress(\"UnstableApiUsage\")\n                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {\n                    try { outputStream.close() } catch (ignored: IOException) {}\n                    val intent = Intent()\n                    intent.data = photoFile.uri\n                    setResult(RESULT_OK, intent)\n                    finish()\n                    Log.d(TAG, \"onImageSaved: \" + photoFile.uri)\n                }\n\n                override fun onError(exception: ImageCaptureException) {\n                    Log.e(TAG, \"onError: \", exception)\n                    try { outputStream.close() } catch (ignored: IOException) {}\n                }\n            }\n        )\n        // We can only change the foreground Drawable using API level 23+ API\n        // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n        //     // Display flash animation to indicate that photo was captured\n        //     final ConstraintLayout container = binding.getRoot();\n        //     container.postDelayed(() -> {\n        //         container.setForeground(new ColorDrawable(Color.WHITE));\n        //         container.postDelayed(() -> container.setForeground(null), 50);\n        //     }, 100);\n        // }\n    }\n\n    /**\n     * Enabled or disabled a button to switch cameras depending on the available cameras\n     */\n    private fun updateCameraSwitchButton() {\n        try {\n            binding.switchCamera.isEnabled = hasBackCamera() && hasFrontCamera()\n        } catch (e: CameraInfoUnavailableException) {\n            binding.switchCamera.isEnabled = false\n        }\n    }\n\n    /**\n     * Returns true if the device has an available back camera. False otherwise\n     */\n    @Throws(CameraInfoUnavailableException::class)\n    private fun hasBackCamera(): Boolean {\n        return if (cameraProvider == null) false else cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false\n    }\n\n    /**\n     * Returns true if the device has an available front camera. False otherwise\n     */\n    @Throws(CameraInfoUnavailableException::class)\n    private fun hasFrontCamera(): Boolean {\n        return if (cameraProvider == null) {\n            false\n        } else cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/activities/DirectorySelectActivity.kt",
    "content": "package awais.instagrabber.activities\n\nimport android.annotation.SuppressLint\nimport android.content.ActivityNotFoundException\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Bundle\nimport android.provider.DocumentsContract\nimport android.util.Log\nimport android.view.View\nimport androidx.activity.viewModels\nimport awais.instagrabber.R\nimport awais.instagrabber.databinding.ActivityDirectorySelectBinding\nimport awais.instagrabber.dialogs.ConfirmDialogFragment\nimport awais.instagrabber.utils.AppExecutors.mainThread\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.viewmodels.DirectorySelectActivityViewModel\nimport java.io.IOException\nimport java.io.PrintWriter\nimport java.io.StringWriter\n\nclass DirectorySelectActivity : BaseLanguageActivity() {\n    private var initialUri: Uri? = null\n\n    private lateinit var binding: ActivityDirectorySelectBinding\n\n    private val viewModel: DirectorySelectActivityViewModel by viewModels()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityDirectorySelectBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n        val intent = intent\n        viewModel.setInitialUri(intent)\n        setupObservers()\n        binding.selectDir.setOnClickListener { openDirectoryChooser() }\n        initialUri = intent.getParcelableExtra(Constants.EXTRA_INITIAL_URI)\n    }\n\n    private fun setupObservers() {\n        viewModel.message.observe(this, { message: String? -> binding.message.text = message })\n        viewModel.prevUri.observe(this, { prevUri: String? ->\n            if (prevUri == null) {\n                binding.prevUri.visibility = View.GONE\n                binding.message2.visibility = View.GONE\n                return@observe\n            }\n            binding.prevUri.text = prevUri\n            binding.prevUri.visibility = View.VISIBLE\n            binding.message2.visibility = View.VISIBLE\n        })\n        viewModel.dirSuccess.observe(this, { success: Boolean -> binding.selectDir.visibility = if (success) View.GONE else View.VISIBLE })\n        viewModel.loading.observe(this, { loading: Boolean ->\n            binding.message.visibility = if (loading) View.GONE else View.VISIBLE\n            binding.loadingIndicator.visibility = if (loading) View.VISIBLE else View.GONE\n        })\n    }\n\n    private fun openDirectoryChooser() {\n        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {\n            intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri)\n        }\n        try {\n            startActivityForResult(intent, SELECT_DIR_REQUEST_CODE)\n        } catch (e: ActivityNotFoundException) {\n            Log.e(TAG, \"openDirectoryChooser: \", e)\n            showErrorDialog(getString(R.string.no_directory_picker_activity))\n        } catch (e: Exception) {\n            Log.e(TAG, \"openDirectoryChooser: \", e)\n        }\n    }\n\n    @SuppressLint(\"StringFormatInvalid\")\n    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        super.onActivityResult(requestCode, resultCode, data)\n        if (requestCode != SELECT_DIR_REQUEST_CODE) return\n        if (resultCode != RESULT_OK) {\n            showErrorDialog(getString(R.string.select_a_folder))\n            return\n        }\n        if (data == null || data.data == null) {\n            showErrorDialog(getString(R.string.select_a_folder))\n            return\n        }\n        val authority = data.data?.authority\n        if (\"com.android.externalstorage.documents\" != authority) {\n            showErrorDialog(getString(R.string.dir_select_no_download_folder, authority))\n            return\n        }\n        mainThread.execute({\n            try {\n                viewModel.setupSelectedDir(data)\n                val intent = Intent(this, MainActivity::class.java)\n                startActivity(intent)\n                finish()\n            } catch (e: Exception) {\n                // Should not come to this point.\n                // If it does, we have to show this error to the user so that they can report it.\n                try {\n                    StringWriter().use { sw ->\n                        PrintWriter(sw).use { pw ->\n                            e.printStackTrace(pw)\n                            showErrorDialog(\"Please report this error to the developers:\\n\\n$sw\")\n                        }\n                    }\n                } catch (ioException: IOException) {\n                    Log.e(TAG, \"onActivityResult: \", ioException)\n                }\n            }\n        }, 500)\n    }\n\n    private fun showErrorDialog(message: String) {\n        val dialogFragment = ConfirmDialogFragment.newInstance(\n            ERROR_REQUEST_CODE,\n            R.string.error,\n            message,\n            R.string.ok,\n            0,\n            0\n        )\n        dialogFragment.show(supportFragmentManager, ConfirmDialogFragment::class.java.simpleName)\n    }\n\n    companion object {\n        const val SELECT_DIR_REQUEST_CODE = 0x01\n        private const val ERROR_REQUEST_CODE = 0x02\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/activities/Login.kt",
    "content": "package awais.instagrabber.activities\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.webkit.*\nimport android.widget.Toast\nimport awais.instagrabber.R\nimport awais.instagrabber.databinding.ActivityLoginBinding\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.getCookie\n\nclass Login : BaseLanguageActivity(), View.OnClickListener {\n    private var webViewUrl: String? = null\n    private var ready = false\n    private lateinit var loginBinding: ActivityLoginBinding\n\n    private val webChromeClient = WebChromeClient()\n    private val webViewClient: WebViewClient = object : WebViewClient() {\n        override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {\n            webViewUrl = url\n        }\n\n        override fun onPageFinished(view: WebView, url: String) {\n            webViewUrl = url\n            val mainCookie = getCookie(url)\n            if (mainCookie.isNullOrBlank() || !mainCookie.contains(\"; ds_user_id=\")) {\n                ready = true\n                return\n            }\n            if (mainCookie.contains(\"; ds_user_id=\") && ready) {\n                returnCookieResult(mainCookie)\n            }\n        }\n    }\n\n    private fun returnCookieResult(mainCookie: String?) {\n        val intent = Intent()\n        intent.putExtra(\"cookie\", mainCookie)\n        setResult(Constants.LOGIN_RESULT_CODE, intent)\n        finish()\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        loginBinding = ActivityLoginBinding.inflate(LayoutInflater.from(applicationContext))\n        setContentView(loginBinding.root)\n        initWebView()\n        loginBinding.cookies.setOnClickListener(this)\n        loginBinding.refresh.setOnClickListener(this)\n    }\n\n    override fun onClick(v: View) {\n        if (v === loginBinding.refresh) {\n            loginBinding.webView.loadUrl(\"https://instagram.com/\")\n            return\n        }\n        if (v === loginBinding.cookies) {\n            val mainCookie = getCookie(webViewUrl)\n            if (mainCookie.isNullOrBlank() || !mainCookie.contains(\"; ds_user_id=\")) {\n                Toast.makeText(this, R.string.login_error_loading_cookies, Toast.LENGTH_SHORT).show()\n                return\n            }\n            returnCookieResult(mainCookie)\n        }\n    }\n\n    @SuppressLint(\"SetJavaScriptEnabled\")\n    private fun initWebView() {\n        loginBinding.webView.webChromeClient = webChromeClient\n        loginBinding.webView.webViewClient = webViewClient\n        val webSettings = loginBinding.webView.settings\n        webSettings.userAgentString =\n            \"Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36\"\n        webSettings.javaScriptEnabled = true\n        webSettings.domStorageEnabled = true\n        webSettings.setSupportZoom(true)\n        webSettings.builtInZoomControls = true\n        webSettings.displayZoomControls = false\n        webSettings.loadWithOverviewMode = true\n        webSettings.useWideViewPort = true\n        webSettings.allowFileAccessFromFileURLs = true\n        webSettings.allowUniversalAccessFromFileURLs = true\n        webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            CookieManager.getInstance().removeAllCookies(null)\n            CookieManager.getInstance().flush()\n        } else {\n            val cookieSyncMngr = CookieSyncManager.createInstance(applicationContext)\n            cookieSyncMngr.startSync()\n            val cookieManager = CookieManager.getInstance()\n            cookieManager.removeAllCookie()\n            cookieManager.removeSessionCookie()\n            cookieSyncMngr.stopSync()\n            cookieSyncMngr.sync()\n        }\n        loginBinding.webView.loadUrl(\"https://instagram.com/\")\n    }\n\n    override fun onPause() {\n        loginBinding.webView.onPause()\n        super.onPause()\n    }\n\n    override fun onResume() {\n        super.onResume()\n        loginBinding.webView.onResume()\n    }\n\n    override fun onDestroy() {\n        loginBinding.webView.destroy()\n        super.onDestroy()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/activities/MainActivity.kt",
    "content": "package awais.instagrabber.activities\n\nimport android.animation.LayoutTransition\nimport android.app.NotificationChannel\nimport android.app.NotificationManager\nimport android.content.ComponentName\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.os.*\nimport android.provider.DocumentsContract.EXTRA_INITIAL_URI\nimport android.text.Editable\nimport android.util.Log\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.view.View\nimport android.view.WindowManager\nimport androidx.appcompat.widget.Toolbar\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.core.provider.FontRequest\nimport androidx.core.view.ViewCompat\nimport androidx.core.view.WindowCompat\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.emoji.text.EmojiCompat\nimport androidx.emoji.text.EmojiCompat.InitCallback\nimport androidx.emoji.text.FontRequestEmojiCompatConfig\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.navigation.NavController\nimport androidx.navigation.NavDestination\nimport androidx.navigation.NavGraph\nimport androidx.navigation.NavGraphNavigator\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.navigation.ui.*\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.R\nimport awais.instagrabber.customviews.emoji.EmojiVariantManager\nimport awais.instagrabber.customviews.helpers.RootViewDeferringInsetsCallback\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter\nimport awais.instagrabber.databinding.ActivityMainBinding\nimport awais.instagrabber.fragments.main.FeedFragment\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.models.IntentModel\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Tab\nimport awais.instagrabber.models.enums.IntentModelType\nimport awais.instagrabber.services.ActivityCheckerService\nimport awais.instagrabber.services.DMSyncAlarmReceiver\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.AppExecutors.tasksThread\nimport awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.utils.emoji.EmojiParser\nimport awais.instagrabber.viewmodels.AppStateViewModel\nimport awais.instagrabber.viewmodels.DirectInboxViewModel\nimport com.google.android.material.appbar.AppBarLayout\nimport com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior\nimport com.google.android.material.appbar.CollapsingToolbarLayout\nimport com.google.android.material.bottomnavigation.BottomNavigationView\nimport com.google.android.material.textfield.TextInputLayout\nimport com.google.common.collect.ImmutableList\nimport java.util.*\n\n\nclass MainActivity : BaseLanguageActivity() {\n    private lateinit var binding: ActivityMainBinding\n    private lateinit var navController: NavController\n    private lateinit var appBarConfiguration: AppBarConfiguration\n\n    private var searchMenuItem: MenuItem? = null\n    private var startNavRootId: Int = 0\n\n    private var lastSelectedNavMenuId = 0\n    private var isActivityCheckerServiceBound = false\n    private var isLoggedIn = false\n    private var deviceUuid: String? = null\n    private var csrfToken: String? = null\n    private var userId: Long = 0\n    private var toolbarOwner: Fragment? = null\n\n    private lateinit var toolbar: Toolbar\n\n    var currentTabs: List<Tab> = emptyList()\n        private set\n    private var showBottomViewDestinations: List<Int> = emptyList()\n\n    private val serviceConnection: ServiceConnection = object : ServiceConnection {\n        override fun onServiceConnected(name: ComponentName, service: IBinder) {\n            // final ActivityCheckerService.LocalBinder binder = (ActivityCheckerService.LocalBinder) service;\n            // final ActivityCheckerService activityCheckerService = binder.getService();\n            isActivityCheckerServiceBound = true\n        }\n\n        override fun onServiceDisconnected(name: ComponentName) {\n            isActivityCheckerServiceBound = false\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        try {\n            DownloadUtils.init(\n                this,\n                Utils.settingsHelper.getString(PreferenceKeys.PREF_BARINSTA_DIR_URI)\n            )\n        } catch (e: ReselectDocumentTreeException) {\n            super.onCreate(savedInstanceState)\n            val intent = Intent(this, DirectorySelectActivity::class.java)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                intent.putExtra(EXTRA_INITIAL_URI, e.initialUri)\n            }\n            startActivity(intent)\n            finish()\n            return\n        }\n        super.onCreate(savedInstanceState)\n        instance = this\n        binding = ActivityMainBinding.inflate(layoutInflater)\n        toolbar = binding.toolbar\n        setupCookie()\n        if (Utils.settingsHelper.getBoolean(PreferenceKeys.FLAG_SECURE)) {\n            window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)\n        }\n        setContentView(binding.root)\n        setSupportActionBar(binding.toolbar)\n        setupInsetsCallback()\n        createNotificationChannels()\n        val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_nav_host) as NavHostFragment\n        navController = navHostFragment.navController\n        if (savedInstanceState == null) {\n            setupNavigation(true)\n        }\n        if (!BuildConfig.isPre) {\n            val checkUpdates = Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_UPDATES)\n            if (checkUpdates) FlavorTown.updateCheck(this)\n        }\n        FlavorTown.changelogCheck(this)\n        ViewModelProvider(this).get(AppStateViewModel::class.java) // Just initiate the App state here\n        handleIntent(intent)\n        if (isLoggedIn && Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_ACTIVITY)) {\n            bindActivityCheckerService()\n        }\n        // Initialise the internal map\n        tasksThread.execute {\n            EmojiParser.getInstance(this)\n            EmojiVariantManager.getInstance()\n        }\n        initEmojiCompat()\n        // initDmService();\n        initDmUnreadCount()\n        initSearchInput()\n    }\n\n    private fun setupInsetsCallback() {\n        val deferringInsetsCallback = RootViewDeferringInsetsCallback(\n            WindowInsetsCompat.Type.systemBars(),\n            WindowInsetsCompat.Type.ime()\n        )\n        ViewCompat.setWindowInsetsAnimationCallback(binding.root, deferringInsetsCallback)\n        ViewCompat.setOnApplyWindowInsetsListener(binding.root, deferringInsetsCallback)\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n    }\n\n    private fun setupCookie() {\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        userId = 0\n        csrfToken = null\n        if (cookie.isNotBlank()) {\n            userId = getUserIdFromCookie(cookie)\n            csrfToken = getCsrfTokenFromCookie(cookie)\n        }\n        if (cookie.isBlank() || userId == 0L || csrfToken.isNullOrBlank()) {\n            isLoggedIn = false\n            return\n        }\n        deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        if (isEmpty(deviceUuid)) {\n            Utils.settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString())\n        }\n        setupCookies(cookie)\n        isLoggedIn = true\n    }\n\n    @Suppress(\"unused\")\n    private fun initDmService() {\n        if (!isLoggedIn) return\n        val enabled = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH)\n        if (!enabled) return\n        DMSyncAlarmReceiver.setAlarm(this)\n    }\n\n    private fun initDmUnreadCount() {\n        if (!isLoggedIn) return\n        val directInboxViewModel = ViewModelProvider(this).get(DirectInboxViewModel::class.java)\n        directInboxViewModel.unseenCount.observe(this, { unseenCountResource: Resource<Int?>? ->\n            if (unseenCountResource == null) return@observe\n            val unseenCount = unseenCountResource.data\n            setNavBarDMUnreadCountBadge(unseenCount ?: 0)\n        })\n    }\n\n    private fun initSearchInput() {\n        binding.searchInputLayout.setEndIconOnClickListener {\n            val editText = binding.searchInputLayout.editText ?: return@setEndIconOnClickListener\n            editText.setText(\"\")\n        }\n        binding.searchInputLayout.addOnEditTextAttachedListener { textInputLayout: TextInputLayout ->\n            textInputLayout.isEndIconVisible = false\n            val editText = textInputLayout.editText ?: return@addOnEditTextAttachedListener\n            editText.addTextChangedListener(object : TextWatcherAdapter() {\n                override fun afterTextChanged(s: Editable) {\n                    binding.searchInputLayout.isEndIconVisible = !isEmpty(s)\n                }\n            })\n        }\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu): Boolean {\n        menuInflater.inflate(R.menu.main_menu, menu)\n        searchMenuItem = menu.findItem(R.id.search)\n        val currentDestination = navController.currentDestination\n        if (currentDestination != null) {\n            val backStack = navController.backQueue\n            setupMenu(backStack.size, currentDestination.id)\n        }\n        return true\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        if (item.itemId == R.id.search) {\n            try {\n                navController.navigate(getSearchDeepLink())\n                return true\n            } catch (e: Exception) {\n                Log.e(TAG, \"onOptionsItemSelected: \", e)\n            }\n            return false\n        }\n        return super.onOptionsItemSelected(item)\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        // outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, firstFragmentGraphIndex.toString())\n        outState.putString(LAST_SELECT_NAV_MENU_ID, binding.bottomNavView.selectedItemId.toString())\n        super.onSaveInstanceState(outState)\n    }\n\n    override fun onRestoreInstanceState(savedInstanceState: Bundle) {\n        super.onRestoreInstanceState(savedInstanceState)\n        val lastSelected = savedInstanceState[LAST_SELECT_NAV_MENU_ID] as String?\n        if (lastSelected != null) {\n            try {\n                lastSelectedNavMenuId = lastSelected.toInt()\n            } catch (ignored: NumberFormatException) {\n            }\n        }\n        setupNavigation(false)\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        return navController.navigateUp(appBarConfiguration)\n    }\n\n    override fun onNewIntent(intent: Intent) {\n        super.onNewIntent(intent)\n        handleIntent(intent)\n    }\n\n    override fun onDestroy() {\n        try {\n            super.onDestroy()\n        } catch (e: Exception) {\n            Log.e(TAG, \"onDestroy: \", e)\n        }\n        unbindActivityCheckerService()\n        // try {\n        //     RetrofitFactory.getInstance().destroy()\n        // } catch (e: Exception) {\n        //     Log.e(TAG, \"onDestroy: \", e)\n        // }\n        DownloadUtils.destroy()\n        instance = null\n    }\n\n    // override fun onBackPressed() {\n    // Log.d(TAG, \"onBackPressed: \")\n    // navController.navigateUp()\n    //     val backStack = navController.backQueue\n    //     val currentNavControllerBackStack = backStack.size\n    //     if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) {\n    //         finishAfterTransition()\n    //         return\n    //     }\n    //     if (!isFinishing) {\n    //         try {\n    //             super.onBackPressed()\n    //         } catch (e: Exception) {\n    //             Log.e(TAG, \"onBackPressed: \", e)\n    //             finish()\n    //         }\n    //     }\n    // }\n\n    private fun createNotificationChannels() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return\n        val notificationManager = NotificationManagerCompat.from(applicationContext)\n        notificationManager.createNotificationChannel(\n            NotificationChannel(\n                Constants.DOWNLOAD_CHANNEL_ID,\n                Constants.DOWNLOAD_CHANNEL_NAME,\n                NotificationManager.IMPORTANCE_DEFAULT\n            )\n        )\n        notificationManager.createNotificationChannel(\n            NotificationChannel(\n                Constants.ACTIVITY_CHANNEL_ID,\n                Constants.ACTIVITY_CHANNEL_NAME,\n                NotificationManager.IMPORTANCE_DEFAULT\n            )\n        )\n        notificationManager.createNotificationChannel(\n            NotificationChannel(\n                Constants.DM_UNREAD_CHANNEL_ID,\n                Constants.DM_UNREAD_CHANNEL_NAME,\n                NotificationManager.IMPORTANCE_DEFAULT\n            )\n        )\n        val silentNotificationChannel = NotificationChannel(\n            Constants.SILENT_NOTIFICATIONS_CHANNEL_ID,\n            Constants.SILENT_NOTIFICATIONS_CHANNEL_NAME,\n            NotificationManager.IMPORTANCE_LOW\n        )\n        silentNotificationChannel.setSound(null, null)\n        notificationManager.createNotificationChannel(silentNotificationChannel)\n    }\n\n    private fun setupNavigation(setDefaultTabFromSettings: Boolean) {\n        currentTabs = if (isLoggedIn) setupMainBottomNav() else setupAnonBottomNav()\n        showBottomViewDestinations = currentTabs.asSequence().map {\n            it.startDestinationFragmentId\n        }.toMutableList().apply {\n            add(R.id.postViewFragment)\n            add(R.id.favorites_non_top)\n            add(R.id.notifications_viewer_non_top)\n            add(R.id.profile_non_top)\n        }\n        if (setDefaultTabFromSettings) {\n            setSelectedTab(currentTabs)\n        } else {\n            binding.bottomNavView.selectedItemId = lastSelectedNavMenuId\n        }\n        val navigatorProvider = navController.navigatorProvider\n        val navigator = navigatorProvider.getNavigator<NavGraphNavigator>(\"navigation\")\n        val rootNavGraph = NavGraph(navigator)\n        val navInflater = navController.navInflater\n        val topLevelDestinations = currentTabs.map { navInflater.inflate(it.navigationResId) }\n        rootNavGraph.id = R.id.root_nav_graph\n        rootNavGraph.label = \"root_nav_graph\"\n        rootNavGraph.addDestinations(topLevelDestinations)\n        rootNavGraph.setStartDestination(if (startNavRootId != 0) startNavRootId else R.id.profile_nav_graph)\n        navController.graph = rootNavGraph\n        binding.bottomNavView.setupWithNavController(navController)\n        appBarConfiguration = AppBarConfiguration(currentTabs.map { it.startDestinationFragmentId }.toSet())\n        setupActionBarWithNavController(navController, appBarConfiguration)\n        navController.addOnDestinationChangedListener { _: NavController?, destination: NavDestination, arguments: Bundle? ->\n            if (destination.id == R.id.directMessagesThreadFragment && arguments != null) {\n                // Set the thread title earlier for better ux\n                val title = arguments.getString(\"title\")\n                if (!title.isNullOrBlank()) {\n                    supportActionBar?.title = title\n                }\n            }\n            if (destination.id == R.id.profileFragment && arguments != null) {\n                // Set the title to username\n                val username = arguments.getString(\"username\")\n                if (!username.isNullOrBlank()) {\n                    supportActionBar?.title = username.substringAfter(\"@\")\n                }\n            }\n            // below is a hack to check if we are at the end of the current stack, to setup the search view\n            binding.appBarLayout.setExpanded(true, true)\n            val destinationId = destination.id\n            val backStack = navController.backQueue\n            setupMenu(backStack.size, destinationId)\n            val contains = showBottomViewDestinations.contains(destinationId)\n            binding.root.post {\n                binding.bottomNavView.visibility = if (contains) View.VISIBLE else View.GONE\n                // if (contains) {\n                //     behavior?.slideUp(binding.bottomNavView)\n                // }\n            }\n            // explicitly hide keyboard when we navigate\n            val view = currentFocus\n            Utils.hideKeyboard(view)\n        }\n        setupReselection()\n    }\n\n    private fun setupReselection() {\n        binding.bottomNavView.setOnItemReselectedListener {\n            val navHostFragment = (supportFragmentManager.primaryNavigationFragment ?: return@setOnItemReselectedListener) as NavHostFragment\n            val currentFragment = navHostFragment.childFragmentManager.fragments.firstOrNull() ?: return@setOnItemReselectedListener\n            if (currentFragment is FeedFragment) {\n                currentFragment.scrollToTop()\n                return@setOnItemReselectedListener\n            }\n            val currentDestination = navController.currentDestination ?: return@setOnItemReselectedListener\n            val currentTabStartDestId = (navController.getBackStackEntry(it.itemId).destination as NavGraph).startDestinationId\n            if (currentDestination.id == currentTabStartDestId) return@setOnItemReselectedListener\n            navController.popBackStack(currentTabStartDestId, false)\n        }\n    }\n\n    private fun setSelectedTab(tabs: List<Tab>) {\n        val defaultTabResNameString = Utils.settingsHelper.getString(Constants.DEFAULT_TAB)\n        try {\n            var navId = 0\n            if (defaultTabResNameString.isNotBlank()) {\n                navId = resources.getIdentifier(defaultTabResNameString, \"id\", packageName)\n            }\n            val startFragmentNavResId = if (navId <= 0) R.id.profile_nav_graph else navId\n            val tab = tabs.firstOrNull { it.navigationRootId == startFragmentNavResId }\n            // if (index < 0 || index >= tabs.size) index = 0\n            val firstTab = tab ?: tabs[0]\n            startNavRootId = firstTab.navigationRootId\n            binding.bottomNavView.selectedItemId = firstTab.navigationRootId\n        } catch (e: Exception) {\n            Log.e(TAG, \"Error parsing id\", e)\n        }\n    }\n\n    private fun setupAnonBottomNav(): List<Tab> {\n        val selectedItemId = binding.bottomNavView.selectedItemId\n        val anonNavTabs = getAnonNavTabs(this)\n        val menu = binding.bottomNavView.menu\n        menu.clear()\n        for (tab in anonNavTabs) {\n            menu.add(0, tab.navigationRootId, 0, tab.title).setIcon(tab.iconResId)\n        }\n        if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph && selectedItemId != R.id.favorites_nav_graph) {\n            binding.bottomNavView.selectedItemId = R.id.profile_nav_graph\n        }\n        return anonNavTabs\n    }\n\n    private fun setupMainBottomNav(): List<Tab> {\n        val menu = binding.bottomNavView.menu\n        menu.clear()\n        val navTabList = getLoggedInNavTabs(this).first\n        for (tab in navTabList) {\n            menu.add(0, tab.navigationRootId, 0, tab.title).setIcon(tab.iconResId)\n        }\n        return navTabList\n    }\n\n    private fun setupMenu(backStackSize: Int, destinationId: Int) {\n        val searchMenuItem = searchMenuItem ?: return\n        if (backStackSize >= 2 && SEARCH_VISIBLE_DESTINATIONS.contains(destinationId)) {\n            searchMenuItem.isVisible = true\n            return\n        }\n        searchMenuItem.isVisible = false\n    }\n\n    private fun setScrollingBehaviour() {\n        val layoutParams = binding.mainNavHost.layoutParams as CoordinatorLayout.LayoutParams\n        layoutParams.behavior = ScrollingViewBehavior()\n        binding.mainNavHost.requestLayout()\n    }\n\n    private fun removeScrollingBehaviour() {\n        val layoutParams = binding.mainNavHost.layoutParams as CoordinatorLayout.LayoutParams\n        layoutParams.behavior = null\n        binding.mainNavHost.requestLayout()\n    }\n\n    private fun handleIntent(intent: Intent?) {\n        if (intent == null) return\n        val action = intent.action\n        val type = intent.type\n        // Log.d(TAG, action + \" \" + type);\n        if (Intent.ACTION_MAIN == action) return\n        if (Constants.ACTION_SHOW_ACTIVITY == action) {\n            showActivityView()\n            return\n        }\n        if (Constants.ACTION_SHOW_DM_THREAD == action) {\n            showThread(intent)\n            return\n        }\n        if (Intent.ACTION_SEND == action && type != null) {\n            if (type == \"text/plain\") {\n                handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT))\n            }\n            return\n        }\n        if (Intent.ACTION_VIEW == action) {\n            val data = intent.data ?: return\n            handleUrl(data.toString())\n        }\n    }\n\n    private fun showThread(intent: Intent) {\n        val threadId = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID)\n        val threadTitle = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE)\n        navigateToThread(threadId, threadTitle)\n    }\n\n    fun navigateToThread(threadId: String?, threadTitle: String?) {\n        if (threadId == null || threadTitle == null) return\n        try {\n            navController.navigate(getDirectThreadDeepLink(threadId, threadTitle))\n        } catch (e: Exception) {\n            Log.e(TAG, \"navigateToThread: \", e)\n        }\n    }\n\n    private fun handleUrl(url: String?) {\n        if (url == null) return\n        // Log.d(TAG, url);\n        val intentModel = IntentUtils.parseUrl(url) ?: return\n        showView(intentModel)\n    }\n\n    private fun showView(intentModel: IntentModel) {\n        when (intentModel.type) {\n            IntentModelType.USERNAME -> showProfileView(intentModel)\n            IntentModelType.POST -> showPostView(intentModel)\n            IntentModelType.LOCATION -> showLocationView(intentModel)\n            IntentModelType.HASHTAG -> showHashtagView(intentModel)\n            IntentModelType.UNKNOWN -> Log.w(TAG, \"Unknown model type received!\")\n            // else -> Log.w(TAG, \"Unknown model type received!\")\n        }\n    }\n\n    private fun showProfileView(intentModel: IntentModel) {\n        try {\n            val username = intentModel.text\n            navController.navigate(getProfileDeepLink(username))\n        } catch (e: Exception) {\n            Log.e(TAG, \"showProfileView: \", e)\n        }\n    }\n\n    private fun showPostView(intentModel: IntentModel) {\n        val shortCode = intentModel.text\n        // Log.d(TAG, \"shortCode: \" + shortCode);\n        try {\n            navController.navigate(getPostDeepLink(shortCode))\n        } catch (e: Exception) {\n            Log.e(TAG, \"showPostView: \", e)\n        }\n    }\n\n    private fun showLocationView(intentModel: IntentModel) {\n        val locationId = intentModel.text\n        // Log.d(TAG, \"locationId: \" + locationId);\n        try {\n            navController.navigate(getLocationDeepLink(locationId))\n        } catch (e: Exception) {\n            Log.e(TAG, \"showLocationView: \", e)\n        }\n    }\n\n    private fun showHashtagView(intentModel: IntentModel) {\n        val hashtag = intentModel.text\n        // Log.d(TAG, \"hashtag: \" + hashtag);\n        try {\n            navController.navigate(getHashtagDeepLink(hashtag))\n        } catch (e: Exception) {\n            Log.e(TAG, \"showHashtagView: \", e)\n        }\n    }\n\n    private fun showActivityView() {\n        try {\n            navController.navigate(getNotificationsDeepLink(\"notif\"))\n        } catch (e: Exception) {\n            Log.e(TAG, \"showActivityView: \", e)\n        }\n    }\n\n    private fun bindActivityCheckerService() {\n        bindService(Intent(this, ActivityCheckerService::class.java), serviceConnection, BIND_AUTO_CREATE)\n        isActivityCheckerServiceBound = true\n    }\n\n    private fun unbindActivityCheckerService() {\n        if (!isActivityCheckerServiceBound) return\n        unbindService(serviceConnection)\n        isActivityCheckerServiceBound = false\n    }\n\n    val bottomNavView: BottomNavigationView\n        get() = binding.bottomNavView\n\n    // fun setCollapsingView(view: View) {\n    //     try {\n    //         binding.collapsingToolbarLayout.addView(view, 0)\n    //     } catch (e: Exception) {\n    //         Log.e(TAG, \"setCollapsingView: \", e)\n    //     }\n    // }\n    //\n    // fun removeCollapsingView(view: View) {\n    //     try {\n    //         binding.collapsingToolbarLayout.removeView(view)\n    //     } catch (e: Exception) {\n    //         Log.e(TAG, \"removeCollapsingView: \", e)\n    //     }\n    // }\n\n    val collapsingToolbarView: CollapsingToolbarLayout\n        get() = binding.collapsingToolbarLayout\n    val appbarLayout: AppBarLayout\n        get() = binding.appBarLayout\n\n    fun removeLayoutTransition() {\n        binding.root.layoutTransition = null\n    }\n\n    fun setLayoutTransition() {\n        binding.root.layoutTransition = LayoutTransition()\n    }\n\n    private fun initEmojiCompat() {\n        // Use a downloadable font for EmojiCompat\n        val fontRequest = FontRequest(\n            \"com.google.android.gms.fonts\",\n            \"com.google.android.gms\",\n            \"Noto Color Emoji Compat\",\n            R.array.com_google_android_gms_fonts_certs\n        )\n        val config: EmojiCompat.Config = FontRequestEmojiCompatConfig(applicationContext, fontRequest)\n        config.setReplaceAll(true) // .setUseEmojiAsDefaultStyle(true)\n            .registerInitCallback(object : InitCallback() {\n                override fun onInitialized() {\n                    Log.i(TAG, \"EmojiCompat initialized\")\n                }\n\n                override fun onFailed(throwable: Throwable?) {\n                    Log.e(TAG, \"EmojiCompat initialization failed\", throwable)\n                }\n            })\n        EmojiCompat.init(config)\n    }\n\n    val rootView: View\n        get() = binding.root\n\n    private val toolbarLock = Any()\n\n    fun getToolbar() = synchronized(toolbarLock) { this.toolbar }\n\n    fun setToolbar(toolbar: Toolbar, owner: Fragment) = synchronized(toolbarLock) {\n        supportActionBar?.subtitle = null\n        toolbarOwner = owner\n        binding.appBarLayout.visibility = View.GONE\n        removeScrollingBehaviour()\n        setSupportActionBar(toolbar)\n        this.toolbar = toolbar\n        NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)\n    }\n\n    fun resetToolbar(owner: Fragment) = synchronized(toolbarLock) {\n        if (owner != toolbarOwner) return\n        this.toolbar = binding.toolbar\n        setSupportActionBar(binding.toolbar)\n        binding.appBarLayout.visibility = View.VISIBLE\n        setScrollingBehaviour()\n        setupActionBarWithNavController(navController, appBarConfiguration)\n        toolbarOwner = null\n    }\n\n    private fun setNavBarDMUnreadCountBadge(unseenCount: Int) {\n        val badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph)\n        if (unseenCount == 0) {\n            badge.isVisible = false\n            badge.clearNumber()\n            return\n        }\n        if (badge.verticalOffset != 10) {\n            badge.verticalOffset = 10\n        }\n        badge.number = unseenCount\n        badge.isVisible = true\n    }\n\n    fun showSearchView(): TextInputLayout {\n        binding.searchInputLayout.visibility = View.VISIBLE\n        return binding.searchInputLayout\n    }\n\n    fun hideSearchView() {\n        binding.searchInputLayout.visibility = View.GONE\n    }\n\n    companion object {\n        private const val TAG = \"MainActivity\"\n        private const val LAST_SELECT_NAV_MENU_ID = \"lastSelectedNavMenuId\"\n\n        private val SEARCH_VISIBLE_DESTINATIONS: List<Int> = ImmutableList.of(\n            R.id.feedFragment,\n            R.id.profileFragment,\n            R.id.directMessagesInboxFragment,\n            R.id.discoverFragment,\n            R.id.favoritesFragment,\n            R.id.hashTagFragment,\n            R.id.locationFragment\n        )\n\n        @JvmStatic\n        var instance: MainActivity? = null\n            private set\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/AccountSwitcherAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.annotation.SuppressLint;\nimport android.graphics.Typeface;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.PrefAccountSwitcherBinding;\nimport awais.instagrabber.db.entities.Account;\nimport awais.instagrabber.utils.Constants;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class AccountSwitcherAdapter extends ListAdapter<Account, AccountSwitcherAdapter.ViewHolder> {\n    private static final String TAG = \"AccountSwitcherAdapter\";\n    private static final DiffUtil.ItemCallback<Account> DIFF_CALLBACK = new DiffUtil.ItemCallback<Account>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Account oldItem, @NonNull final Account newItem) {\n            return oldItem.getUid().equals(newItem.getUid());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Account oldItem, @NonNull final Account newItem) {\n            return oldItem.getUid().equals(newItem.getUid());\n        }\n    };\n\n    private final OnAccountClickListener clickListener;\n    private final OnAccountLongClickListener longClickListener;\n\n    public AccountSwitcherAdapter(final OnAccountClickListener clickListener,\n                                  final OnAccountLongClickListener longClickListener) {\n        super(DIFF_CALLBACK);\n        this.clickListener = clickListener;\n        this.longClickListener = longClickListener;\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.inflate(layoutInflater, parent, false);\n        return new ViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {\n        final Account model = getItem(position);\n        if (model == null) return;\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isCurrent = model.getCookie().equals(cookie);\n        holder.bind(model, isCurrent, clickListener, longClickListener);\n    }\n\n    public interface OnAccountClickListener {\n        void onAccountClick(final Account model, final boolean isCurrent);\n    }\n\n    public interface OnAccountLongClickListener {\n        boolean onAccountLongClick(final Account model, final boolean isCurrent);\n    }\n\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n        private final PrefAccountSwitcherBinding binding;\n\n        public ViewHolder(final PrefAccountSwitcherBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n            binding.arrowDown.setImageResource(R.drawable.ic_check_24);\n        }\n\n        @SuppressLint(\"SetTextI18n\")\n        public void bind(final Account model,\n                         final boolean isCurrent,\n                         final OnAccountClickListener clickListener,\n                         final OnAccountLongClickListener longClickListener) {\n            // Log.d(TAG, model.getFullName());\n            itemView.setOnClickListener(v -> {\n                if (clickListener == null) return;\n                clickListener.onAccountClick(model, isCurrent);\n            });\n            itemView.setOnLongClickListener(v -> {\n                if (longClickListener == null) return false;\n                return longClickListener.onAccountLongClick(model, isCurrent);\n            });\n            binding.profilePic.setImageURI(model.getProfilePic());\n            binding.username.setText(\"@\" + model.getUsername());\n            binding.fullName.setTypeface(null);\n            final String fullName = model.getFullName();\n            if (TextUtils.isEmpty(fullName)) {\n                binding.fullName.setVisibility(View.GONE);\n            } else {\n                binding.fullName.setVisibility(View.VISIBLE);\n                binding.fullName.setText(fullName);\n            }\n            if (!isCurrent) {\n                binding.arrowDown.setVisibility(View.GONE);\n                return;\n            }\n            binding.fullName.setTypeface(binding.fullName.getTypeface(), Typeface.BOLD);\n            binding.arrowDown.setVisibility(View.VISIBLE);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.adapters.viewholder.CommentViewHolder;\nimport awais.instagrabber.databinding.ItemCommentBinding;\nimport awais.instagrabber.models.Comment;\n\npublic final class CommentsAdapter extends ListAdapter<Comment, CommentViewHolder> {\n    private static final DiffUtil.ItemCallback<Comment> DIFF_CALLBACK = new DiffUtil.ItemCallback<Comment>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Comment oldItem, @NonNull final Comment newItem) {\n            return Objects.equals(oldItem.getPk(), newItem.getPk());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Comment oldItem, @NonNull final Comment newItem) {\n            return Objects.equals(oldItem, newItem);\n        }\n    };\n\n    private final boolean showingReplies;\n    private final CommentCallback commentCallback;\n    private final long currentUserId;\n\n    public CommentsAdapter(final long currentUserId,\n                           final boolean showingReplies,\n                           final CommentCallback commentCallback) {\n        super(DIFF_CALLBACK);\n        this.showingReplies = showingReplies;\n        this.currentUserId = currentUserId;\n        this.commentCallback = commentCallback;\n    }\n\n    @NonNull\n    @Override\n    public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);\n        return new CommentViewHolder(binding, currentUserId, commentCallback);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) {\n        final Comment comment = getItem(position);\n        holder.bind(comment, showingReplies && position == 0, showingReplies && position != 0);\n    }\n\n    public interface CommentCallback {\n        void onClick(final Comment comment);\n\n        void onHashtagClick(final String hashtag);\n\n        void onMentionClick(final String mention);\n\n        void onURLClick(final String url);\n\n        void onEmailClick(final String emailAddress);\n\n        void onLikeClick(final Comment comment, boolean liked, final boolean isReply);\n\n        void onRepliesClick(final Comment comment);\n\n        void onViewLikes(Comment comment);\n\n        void onTranslate(Comment comment);\n\n        void onDelete(Comment comment, boolean isReply);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.IdRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.AdapterListUpdateCallback;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.AsyncListDiffer;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemActionLogViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemAnimatedMediaViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemDefaultViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemLikeViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemLinkViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaShareViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemPlaceholderViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemProfileViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemRavenMediaViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemReelShareViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemStoryShareViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemTextViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemVideoCallEventViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemVoiceMediaViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemXmaViewHolder;\nimport awais.instagrabber.customviews.emoji.Emoji;\nimport awais.instagrabber.databinding.LayoutDmActionLogBinding;\nimport awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmHeaderBinding;\nimport awais.instagrabber.databinding.LayoutDmLikeBinding;\nimport awais.instagrabber.databinding.LayoutDmLinkBinding;\nimport awais.instagrabber.databinding.LayoutDmMediaBinding;\nimport awais.instagrabber.databinding.LayoutDmMediaShareBinding;\nimport awais.instagrabber.databinding.LayoutDmProfileBinding;\nimport awais.instagrabber.databinding.LayoutDmRavenMediaBinding;\nimport awais.instagrabber.databinding.LayoutDmReelShareBinding;\nimport awais.instagrabber.databinding.LayoutDmStoryShareBinding;\nimport awais.instagrabber.databinding.LayoutDmTextBinding;\nimport awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\n\npublic final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n    private static final String TAG = DirectItemsAdapter.class.getSimpleName();\n\n    private List<DirectItem> items;\n    private DirectThread thread;\n    private DirectItemViewHolder selectedViewHolder;\n\n    private final User currentUser;\n    private final DirectItemCallback callback;\n    private final AsyncListDiffer<DirectItemOrHeader> differ;\n    private final DirectItemInternalLongClickListener longClickListener;\n\n    private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {\n            final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();\n            final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();\n            boolean areSameType = bothHeaders || bothItems;\n            if (!areSameType) return false;\n            if (bothHeaders) {\n                return oldItem.date.equals(newItem.date);\n            }\n            if (oldItem.item != null && newItem.item != null) {\n                String oldClientContext = oldItem.item.getClientContext();\n                if (oldClientContext == null) {\n                    oldClientContext = oldItem.item.getItemId();\n                }\n                String newClientContext = newItem.item.getClientContext();\n                if (newClientContext == null) {\n                    newClientContext = newItem.item.getItemId();\n                }\n                return oldClientContext.equals(newClientContext);\n            }\n            return false;\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {\n            final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();\n            final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();\n            boolean areSameType = bothHeaders || bothItems;\n            if (!areSameType) return false;\n            if (bothHeaders) {\n                return oldItem.date.equals(newItem.date);\n            }\n            final boolean timestampEqual = oldItem.item.getTimestamp() == newItem.item.getTimestamp();\n            final boolean bothPending = oldItem.item.isPending() == newItem.item.isPending();\n            final boolean reactionSame = Objects.equals(oldItem.item.getReactions(), newItem.item.getReactions());\n            return timestampEqual && bothPending && reactionSame;\n        }\n    };\n\n    public DirectItemsAdapter(@NonNull final User currentUser,\n                              @NonNull final DirectThread thread,\n                              @NonNull final DirectItemCallback callback,\n                              @NonNull final DirectItemLongClickListener itemLongClickListener) {\n        this.currentUser = currentUser;\n        this.thread = thread;\n        this.callback = callback;\n        differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),\n                                       new AsyncDifferConfig.Builder<>(diffCallback).build());\n        longClickListener = (position, viewHolder) -> {\n            if (selectedViewHolder != null) {\n                selectedViewHolder.setSelected(false);\n            }\n            selectedViewHolder = viewHolder;\n            viewHolder.setSelected(true);\n            itemLongClickListener.onLongClick(position);\n        };\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        if (type == -1) {\n            // header\n            return new HeaderViewHolder(LayoutDmHeaderBinding.inflate(layoutInflater, parent, false));\n        }\n        final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);\n        final DirectItemType directItemType = DirectItemType.Companion.getTypeFromId(type);\n        final DirectItemViewHolder itemViewHolder = getItemViewHolder(layoutInflater, baseBinding, directItemType);\n        itemViewHolder.setLongClickListener(longClickListener);\n        return itemViewHolder;\n    }\n\n    @NonNull\n    private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater,\n                                                   final LayoutDmBaseBinding baseBinding,\n                                                   @NonNull final DirectItemType directItemType) {\n        switch (directItemType) {\n            case TEXT: {\n                final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemTextViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case LIKE: {\n                final LayoutDmLikeBinding binding = LayoutDmLikeBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemLikeViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case LINK: {\n                final LayoutDmLinkBinding binding = LayoutDmLinkBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemLinkViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case ACTION_LOG: {\n                final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemActionLogViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case VIDEO_CALL_EVENT: {\n                final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemVideoCallEventViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case PLACEHOLDER: {\n                final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemPlaceholderViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case ANIMATED_MEDIA: {\n                final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemAnimatedMediaViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case VOICE_MEDIA: {\n                final LayoutDmVoiceMediaBinding binding = LayoutDmVoiceMediaBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemVoiceMediaViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case LOCATION:\n            case PROFILE: {\n                final LayoutDmProfileBinding binding = LayoutDmProfileBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemProfileViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case MEDIA: {\n                final LayoutDmMediaBinding binding = LayoutDmMediaBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemMediaViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case CLIP:\n            case FELIX_SHARE:\n            case MEDIA_SHARE: {\n                final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemMediaShareViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case STORY_SHARE: {\n                final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemStoryShareViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case REEL_SHARE: {\n                final LayoutDmReelShareBinding binding = LayoutDmReelShareBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemReelShareViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case RAVEN_MEDIA: {\n                final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemRavenMediaViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case XMA: {\n                final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemXmaViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n            case UNKNOWN:\n            default: {\n                final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);\n                return new DirectItemDefaultViewHolder(baseBinding, binding, currentUser, thread, callback);\n            }\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        final DirectItemOrHeader itemOrHeader = getItem(position);\n        if (itemOrHeader.isHeader()) {\n            ((HeaderViewHolder) holder).bind(itemOrHeader.date);\n            return;\n        }\n        if (thread == null) return;\n        ((DirectItemViewHolder) holder).bind(position, itemOrHeader.item);\n    }\n\n    protected DirectItemOrHeader getItem(int position) {\n        return differ.getCurrentList().get(position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        final DirectItemOrHeader itemOrHeader = getItem(position);\n        if (itemOrHeader.isHeader()) {\n            return -1;\n        }\n        final DirectItemType itemType = itemOrHeader.item.getItemType();\n        if (itemType == null) {\n            return 0;\n        }\n        return itemType.getId();\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        final DirectItemOrHeader itemOrHeader = getItem(position);\n        if (itemOrHeader.isHeader()) {\n            return itemOrHeader.date.hashCode();\n        }\n        if (itemOrHeader.item.getClientContext() == null) {\n            return itemOrHeader.item.getItemId().hashCode();\n        }\n        return itemOrHeader.item.getClientContext().hashCode();\n    }\n\n    public void setThread(final DirectThread thread) {\n        if (thread == null) return;\n        this.thread = thread;\n        // notifyDataSetChanged();\n    }\n\n    public void submitList(@Nullable final List<DirectItem> list) {\n        if (list == null) {\n            differ.submitList(null);\n            return;\n        }\n        differ.submitList(sectionAndSort(list));\n        this.items = list;\n    }\n\n    public void submitList(@Nullable final List<DirectItem> list, @Nullable final Runnable commitCallback) {\n        if (list == null) {\n            differ.submitList(null, commitCallback);\n            return;\n        }\n        differ.submitList(sectionAndSort(list), commitCallback);\n        this.items = list;\n    }\n\n    private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) {\n        final List<DirectItemOrHeader> itemOrHeaders = new ArrayList<>();\n        LocalDate prevSectionDate = null;\n        for (int i = 0; i < list.size(); i++) {\n            final DirectItem item = list.get(i);\n            if (item == null || item.getDate() == null) continue;\n            final DirectItemOrHeader prev = itemOrHeaders.isEmpty() ? null : itemOrHeaders.get(itemOrHeaders.size() - 1);\n            if (prev != null\n                    && prev.item != null\n                    && prev.item.getDate() != null\n                    && prev.item.getDate().toLocalDate().isEqual(item.getDate().toLocalDate())) {\n                // just add item\n                final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();\n                itemOrHeader.item = item;\n                itemOrHeaders.add(itemOrHeader);\n                if (i == list.size() - 1) {\n                    // add header\n                    final DirectItemOrHeader itemOrHeader2 = new DirectItemOrHeader();\n                    itemOrHeader2.date = prevSectionDate;\n                    itemOrHeaders.add(itemOrHeader2);\n                }\n                continue;\n            }\n            if (prevSectionDate != null) {\n                // add header\n                final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();\n                itemOrHeader.date = prevSectionDate;\n                itemOrHeaders.add(itemOrHeader);\n            }\n            // Add item\n            final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();\n            itemOrHeader.item = item;\n            itemOrHeaders.add(itemOrHeader);\n            prevSectionDate = item.getDate().toLocalDate();\n        }\n        return itemOrHeaders;\n    }\n\n    public List<DirectItemOrHeader> getList() {\n        return differ.getCurrentList();\n    }\n\n    public List<DirectItem> getItems() {\n        return items;\n    }\n\n    @Override\n    public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) {\n        if (holder instanceof DirectItemViewHolder) {\n            ((DirectItemViewHolder) holder).cleanup();\n        }\n    }\n\n    @Override\n    public void onViewDetachedFromWindow(@NonNull final RecyclerView.ViewHolder holder) {\n        if (holder instanceof DirectItemViewHolder) {\n            ((DirectItemViewHolder) holder).cleanup();\n        }\n    }\n\n    public DirectThread getThread() {\n        return thread;\n    }\n\n    public static class DirectItemOrHeader {\n        LocalDate date;\n        public DirectItem item;\n\n        public boolean isHeader() {\n            return date != null;\n        }\n\n        @NonNull\n        @Override\n        public String toString() {\n            return \"DirectItemOrHeader{\" +\n                    \"date=\" + date +\n                    \", item=\" + (item != null ? item.getItemType() : null) +\n                    '}';\n        }\n    }\n\n    public static class HeaderViewHolder extends RecyclerView.ViewHolder {\n        private final LayoutDmHeaderBinding binding;\n\n        public HeaderViewHolder(@NonNull final LayoutDmHeaderBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(final LocalDate date) {\n            if (date == null) {\n                binding.header.setText(\"\");\n                return;\n            }\n            final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);\n            binding.header.setText(dateFormatter.format(date));\n        }\n    }\n\n    public interface DirectItemCallback {\n        void onHashtagClick(String hashtag);\n\n        void onMentionClick(String mention);\n\n        void onLocationClick(long locationId);\n\n        void onURLClick(String url);\n\n        void onEmailClick(String email);\n\n        void onMediaClick(Media media, int index);\n\n        void onStoryClick(DirectItemStoryShare storyShare);\n\n        void onReaction(DirectItem item, Emoji emoji);\n\n        void onReactionClick(DirectItem item, int position);\n\n        void onOptionSelect(DirectItem item, @IdRes int itemId, final Function<DirectItem, Void> callback);\n\n        void onAddReactionListener(DirectItem item);\n    }\n\n    public interface DirectItemInternalLongClickListener {\n        void onLongClick(int position, DirectItemViewHolder viewHolder);\n    }\n\n    public interface DirectItemLongClickListener {\n        void onLongClick(int position);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectInboxItemViewHolder;\nimport awais.instagrabber.databinding.LayoutDmInboxItemBinding;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\n\npublic final class DirectMessageInboxAdapter extends ListAdapter<DirectThread, DirectInboxItemViewHolder> {\n    private final OnItemClickListener onClickListener;\n\n    private static final DiffUtil.ItemCallback<DirectThread> diffCallback = new DiffUtil.ItemCallback<DirectThread>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final DirectThread oldItem, @NonNull final DirectThread newItem) {\n            return oldItem.getThreadId().equals(newItem.getThreadId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final DirectThread oldThread,\n                                          @NonNull final DirectThread newThread) {\n            final boolean titleEqual = oldThread.getThreadTitle().equals(newThread.getThreadTitle());\n            if (!titleEqual) return false;\n            final boolean lastSeenAtEqual = Objects.equals(oldThread.getLastSeenAt(), newThread.getLastSeenAt());\n            if (!lastSeenAtEqual) return false;\n            final List<DirectItem> oldItems = oldThread.getItems();\n            final List<DirectItem> newItems = newThread.getItems();\n            if (oldItems == null || newItems == null) return false;\n            if (oldItems.size() != newItems.size()) return false;\n            final DirectItem oldItemFirst = oldThread.getFirstDirectItem();\n            final DirectItem newItemFirst = newThread.getFirstDirectItem();\n            if (oldItemFirst == null || newItemFirst == null) return false;\n            final boolean idsEqual = oldItemFirst.getItemId().equals(newItemFirst.getItemId());\n            if (!idsEqual) return false;\n            return oldItemFirst.getTimestamp() == newItemFirst.getTimestamp();\n        }\n    };\n\n    public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) {\n        super(new AsyncDifferConfig.Builder<>(diffCallback).build());\n        this.onClickListener = onClickListener;\n    }\n\n    @NonNull\n    @Override\n    public DirectInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);\n        return new DirectInboxItemViewHolder(binding, onClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final DirectInboxItemViewHolder holder, final int position) {\n        final DirectThread thread = getItem(position);\n        holder.bind(thread);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return getItem(position).getThreadId().hashCode();\n    }\n\n    public interface OnItemClickListener {\n        void onItemClick(final DirectThread thread);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectPendingUsersAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectPendingUserViewHolder;\nimport awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;\n\npublic final class DirectPendingUsersAdapter extends ListAdapter<DirectPendingUsersAdapter.PendingUser, DirectPendingUserViewHolder> {\n\n    private static final DiffUtil.ItemCallback<PendingUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<PendingUser>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {\n            return oldItem.user.getPk() == newItem.user.getPk();\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {\n            return Objects.equals(oldItem.user.getUsername(), newItem.user.getUsername()) &&\n                    Objects.equals(oldItem.user.getFullName(), newItem.user.getFullName()) &&\n                    Objects.equals(oldItem.requester, newItem.requester);\n        }\n    };\n\n    private final PendingUserCallback callback;\n\n    public DirectPendingUsersAdapter(final PendingUserCallback callback) {\n        super(DIFF_CALLBACK);\n        this.callback = callback;\n        setHasStableIds(true);\n    }\n\n    public void submitPendingRequests(final DirectThreadParticipantRequestsResponse requests) {\n        if (requests == null || requests.getUsers() == null) {\n            submitList(Collections.emptyList());\n            return;\n        }\n        submitList(parse(requests));\n    }\n\n    private List<PendingUser> parse(final DirectThreadParticipantRequestsResponse requests) {\n        final List<User> users = requests.getUsers();\n        final Map<Long, String> requesterUsernames = requests.getRequesterUsernames();\n        return users.stream()\n                    .map(user -> new PendingUser(user, requesterUsernames.get(user.getPk())))\n                    .collect(Collectors.toList());\n    }\n\n    @NonNull\n    @Override\n    public DirectPendingUserViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final LayoutDmPendingUserItemBinding binding = LayoutDmPendingUserItemBinding.inflate(layoutInflater, parent, false);\n        return new DirectPendingUserViewHolder(binding, callback);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final DirectPendingUserViewHolder holder, final int position) {\n        final PendingUser pendingUser = getItem(position);\n        holder.bind(position, pendingUser);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        final PendingUser item = getItem(position);\n        return item.user.getPk();\n    }\n\n    public static class PendingUser {\n        private final User user;\n        private final String requester;\n\n        private boolean inProgress;\n\n        public PendingUser(final User user, final String requester) {\n            this.user = user;\n            this.requester = requester;\n        }\n\n        public User getUser() {\n            return user;\n        }\n\n        public String getRequester() {\n            return requester;\n        }\n\n        public boolean isInProgress() {\n            return inProgress;\n        }\n\n        public PendingUser setInProgress(final boolean inProgress) {\n            this.inProgress = inProgress;\n            return this;\n        }\n    }\n\n    public interface PendingUserCallback {\n        void onClick(int position, PendingUser pendingUser);\n\n        void onApprove(int position, PendingUser pendingUser);\n\n        void onDeny(int position, PendingUser pendingUser);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectReactionsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.List;\n\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectReactionViewHolder;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;\n\npublic final class DirectReactionsAdapter extends ListAdapter<DirectItemEmojiReaction, DirectReactionViewHolder> {\n\n    private static final DiffUtil.ItemCallback<DirectItemEmojiReaction> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectItemEmojiReaction>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {\n            return oldItem.getSenderId() == newItem.getSenderId();\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {\n            return oldItem.getEmoji().equals(newItem.getEmoji());\n        }\n    };\n\n    private final long viewerId;\n    private final List<User> users;\n    private final String itemId;\n    private final OnReactionClickListener onReactionClickListener;\n\n    public DirectReactionsAdapter(final long viewerId,\n                                  final List<User> users,\n                                  final String itemId,\n                                  final OnReactionClickListener onReactionClickListener) {\n        super(DIFF_CALLBACK);\n        this.viewerId = viewerId;\n        this.users = users;\n        this.itemId = itemId;\n        this.onReactionClickListener = onReactionClickListener;\n        setHasStableIds(true);\n    }\n\n    @NonNull\n    @Override\n    public DirectReactionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);\n        return new DirectReactionViewHolder(binding, viewerId, itemId, onReactionClickListener);\n\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final DirectReactionViewHolder holder, final int position) {\n        final DirectItemEmojiReaction reaction = getItem(position);\n        if (reaction == null) return;\n        holder.bind(reaction, getUser(reaction.getSenderId()));\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return getItem(position).getSenderId();\n    }\n\n    @Nullable\n    private User getUser(final long pk) {\n        return users.stream()\n                    .filter(user -> user.getPk() == pk)\n                    .findFirst()\n                    .orElse(null);\n    }\n\n    public interface OnReactionClickListener {\n        void onReactionClick(String itemId, DirectItemEmojiReaction reaction);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectUsersAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;\nimport awais.instagrabber.databinding.ItemFavSectionHeaderBinding;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\n\npublic final class DirectUsersAdapter extends ListAdapter<DirectUsersAdapter.DirectUserOrHeader, RecyclerView.ViewHolder> {\n\n    private static final int VIEW_TYPE_HEADER = 0;\n    private static final int VIEW_TYPE_USER = 1;\n    private static final DiffUtil.ItemCallback<DirectUserOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectUserOrHeader>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {\n            final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();\n            final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();\n            boolean areSameType = bothHeaders || bothItems;\n            if (!areSameType) return false;\n            if (bothHeaders) {\n                return oldItem.headerTitle == newItem.headerTitle;\n            }\n            if (oldItem.user != null && newItem.user != null) {\n                return oldItem.user.getPk() == newItem.user.getPk();\n            }\n            return false;\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {\n            final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();\n            final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();\n            boolean areSameType = bothHeaders || bothItems;\n            if (!areSameType) return false;\n            if (bothHeaders) {\n                return oldItem.headerTitle == newItem.headerTitle;\n            }\n            if (oldItem.user != null && newItem.user != null) {\n                return oldItem.user.getUsername().equals(newItem.user.getUsername()) &&\n                        oldItem.user.getFullName().equals(newItem.user.getFullName());\n            }\n            return false;\n        }\n    };\n\n    private final long inviterId;\n    private final OnDirectUserClickListener onClickListener;\n    private final OnDirectUserLongClickListener onLongClickListener;\n    private List<Long> adminUserIds;\n\n    public DirectUsersAdapter(final long inviterId,\n                              final OnDirectUserClickListener onClickListener,\n                              final OnDirectUserLongClickListener onLongClickListener) {\n        super(DIFF_CALLBACK);\n        this.inviterId = inviterId;\n        this.onClickListener = onClickListener;\n        this.onLongClickListener = onLongClickListener;\n        setHasStableIds(true);\n    }\n\n    public void submitUsers(final List<User> users, final List<User> leftUsers) {\n        if (users == null && leftUsers == null) return;\n        final List<DirectUserOrHeader> userOrHeaders = combineLists(users, leftUsers);\n        submitList(userOrHeaders);\n    }\n\n    private List<DirectUserOrHeader> combineLists(final List<User> users, final List<User> leftUsers) {\n        final ImmutableList.Builder<DirectUserOrHeader> listBuilder = ImmutableList.builder();\n        if (users != null && !users.isEmpty()) {\n            listBuilder.add(new DirectUserOrHeader(R.string.members));\n            users.stream()\n                 .map(DirectUserOrHeader::new)\n                 .forEach(listBuilder::add);\n        }\n        if (leftUsers != null && !leftUsers.isEmpty()) {\n            listBuilder.add(new DirectUserOrHeader(R.string.dms_left_users));\n            leftUsers.stream()\n                     .map(DirectUserOrHeader::new)\n                     .forEach(listBuilder::add);\n        }\n        return listBuilder.build();\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        switch (viewType) {\n            case VIEW_TYPE_USER:\n                final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);\n                return new DirectUserViewHolder(binding, onClickListener, onLongClickListener);\n            case VIEW_TYPE_HEADER:\n            default:\n                final ItemFavSectionHeaderBinding headerBinding = ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false);\n                return new HeaderViewHolder(headerBinding);\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        if (holder instanceof HeaderViewHolder) {\n            ((HeaderViewHolder) holder).bind(getItem(position).headerTitle);\n            return;\n        }\n        if (holder instanceof DirectUserViewHolder) {\n            final User user = getItem(position).user;\n            ((DirectUserViewHolder) holder).bind(position,\n                                                 user,\n                                                 user != null && adminUserIds != null && adminUserIds.contains(user.getPk()),\n                                                 user != null && user.getPk() == inviterId,\n                                                 false,\n                                                 false);\n        }\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        final DirectUserOrHeader item = getItem(position);\n        return item.isHeader() ? VIEW_TYPE_HEADER : VIEW_TYPE_USER;\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        final DirectUserOrHeader item = getItem(position);\n        return item.isHeader() ? item.headerTitle : item.user.getPk();\n    }\n\n    public void setAdminUserIds(final List<Long> adminUserIds) {\n        this.adminUserIds = adminUserIds;\n        notifyDataSetChanged();\n    }\n\n    public static class DirectUserOrHeader {\n        int headerTitle;\n        User user;\n\n        public DirectUserOrHeader(final int headerTitle) {\n            this.headerTitle = headerTitle;\n        }\n\n        public DirectUserOrHeader(final User user) {\n            this.user = user;\n        }\n\n        boolean isHeader() {\n            return headerTitle > 0;\n        }\n    }\n\n    public static class HeaderViewHolder extends RecyclerView.ViewHolder {\n        private final ItemFavSectionHeaderBinding binding;\n\n        public HeaderViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(@StringRes final int headerTitle) {\n            binding.getRoot().setText(headerTitle);\n        }\n    }\n\n    public interface OnDirectUserClickListener {\n        void onClick(int position, User user, boolean selected);\n    }\n\n    public interface OnDirectUserLongClickListener {\n        boolean onLongClick(int position, User user);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DirectoryFilesAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.io.File;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.ItemDirListBinding;\n\npublic final class DirectoryFilesAdapter extends ListAdapter<File, DirectoryFilesAdapter.ViewHolder> {\n    private final OnFileClickListener onFileClickListener;\n\n    private static final DiffUtil.ItemCallback<File> DIFF_CALLBACK = new DiffUtil.ItemCallback<File>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final File oldItem, @NonNull final File newItem) {\n            return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final File oldItem, @NonNull final File newItem) {\n            return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath());\n        }\n    };\n\n    public DirectoryFilesAdapter(final OnFileClickListener onFileClickListener) {\n        super(DIFF_CALLBACK);\n        this.onFileClickListener = onFileClickListener;\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());\n        final ItemDirListBinding binding = ItemDirListBinding.inflate(inflater, parent, false);\n        return new ViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {\n        final File file = getItem(position);\n        holder.bind(file, onFileClickListener);\n    }\n\n    public interface OnFileClickListener {\n        void onFileClick(File file);\n    }\n\n    static final class ViewHolder extends RecyclerView.ViewHolder {\n        private final ItemDirListBinding binding;\n\n        private ViewHolder(final ItemDirListBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(final File file, final OnFileClickListener onFileClickListener) {\n            if (file == null) return;\n            if (onFileClickListener != null) {\n                itemView.setOnClickListener(v -> onFileClickListener.onFileClick(file));\n            }\n            binding.text.setText(file.getName());\n            if (file.isDirectory()) {\n                binding.icon.setImageResource(R.drawable.ic_folder_24);\n                return;\n            }\n            binding.icon.setImageResource(R.drawable.ic_file_24);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;\nimport awais.instagrabber.databinding.ItemDiscoverTopicBinding;\nimport awais.instagrabber.repositories.responses.discover.TopicCluster;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> {\n    private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {\n            final String oldThumbUrl = ResponseBodyUtils.getThumbUrl(oldItem.getCoverMedia());\n            return oldThumbUrl != null && oldThumbUrl.equals(ResponseBodyUtils.getThumbUrl(newItem.getCoverMedia()))\n                    && oldItem.getTitle().equals(newItem.getTitle());\n        }\n    };\n\n    private final OnTopicClickListener onTopicClickListener;\n\n    public DiscoverTopicsAdapter(final OnTopicClickListener onTopicClickListener) {\n        super(DIFF_CALLBACK);\n        this.onTopicClickListener = onTopicClickListener;\n    }\n\n    @NonNull\n    @Override\n    public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);\n        return new TopicClusterViewHolder(binding, onTopicClickListener, null);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {\n        final TopicCluster topicCluster = getItem(position);\n        holder.bind(topicCluster);\n    }\n\n    public interface OnTopicClickListener {\n        void onTopicClick(TopicCluster topicCluster, View cover, int titleColor, int backgroundColor);\n\n        void onTopicLongClick(Media coverMedia);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FavoritesAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.ObjectsCompat;\nimport androidx.recyclerview.widget.AdapterListUpdateCallback;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.AsyncListDiffer;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.FavoriteViewHolder;\nimport awais.instagrabber.databinding.ItemFavSectionHeaderBinding;\nimport awais.instagrabber.databinding.ItemSearchResultBinding;\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.models.enums.FavoriteType;\n\npublic class FavoritesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n\n    private final OnFavoriteClickListener clickListener;\n    private final OnFavoriteLongClickListener longClickListener;\n    private final AsyncListDiffer<FavoriteModelOrHeader> differ;\n\n    private static final DiffUtil.ItemCallback<FavoriteModelOrHeader> diffCallback = new DiffUtil.ItemCallback<FavoriteModelOrHeader>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final FavoriteModelOrHeader oldItem, @NonNull final FavoriteModelOrHeader newItem) {\n            boolean areSame = oldItem.isHeader() && newItem.isHeader();\n            if (!areSame) {\n                return false;\n            }\n            if (oldItem.isHeader()) {\n                return ObjectsCompat.equals(oldItem.header, newItem.header);\n            }\n            if (oldItem.model != null && newItem.model != null) {\n                return oldItem.model.getId() == newItem.model.getId();\n            }\n            return false;\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final FavoriteModelOrHeader oldItem, @NonNull final FavoriteModelOrHeader newItem) {\n            boolean areSame = oldItem.isHeader() && newItem.isHeader();\n            if (!areSame) {\n                return false;\n            }\n            if (oldItem.isHeader()) {\n                return ObjectsCompat.equals(oldItem.header, newItem.header);\n            }\n            return ObjectsCompat.equals(oldItem.model, newItem.model);\n        }\n    };\n\n    public FavoritesAdapter(final OnFavoriteClickListener clickListener, final OnFavoriteLongClickListener longClickListener) {\n        this.clickListener = clickListener;\n        this.longClickListener = longClickListener;\n        differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),\n                                       new AsyncDifferConfig.Builder<>(diffCallback).build());\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());\n        if (viewType == 0) {\n            // header\n            return new FavSectionViewHolder(ItemFavSectionHeaderBinding.inflate(inflater, parent, false));\n        }\n        final ItemSearchResultBinding binding = ItemSearchResultBinding.inflate(inflater, parent, false);\n        return new FavoriteViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        if (getItemViewType(position) == 0) {\n            final FavoriteModelOrHeader modelOrHeader = getItem(position);\n            if (!modelOrHeader.isHeader()) return;\n            ((FavSectionViewHolder) holder).bind(modelOrHeader.header);\n            return;\n        }\n        ((FavoriteViewHolder) holder).bind(getItem(position).model, clickListener, longClickListener);\n    }\n\n    protected FavoriteModelOrHeader getItem(int position) {\n        return differ.getCurrentList().get(position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return getItem(position).isHeader() ? 0 : 1;\n    }\n\n    public void submitList(@Nullable final List<Favorite> list) {\n        if (list == null) {\n            differ.submitList(null);\n            return;\n        }\n        differ.submitList(sectionAndSort(list));\n    }\n\n    public void submitList(@Nullable final List<Favorite> list, @Nullable final Runnable commitCallback) {\n        if (list == null) {\n            differ.submitList(null, commitCallback);\n            return;\n        }\n        differ.submitList(sectionAndSort(list), commitCallback);\n    }\n\n    @NonNull\n    private List<FavoriteModelOrHeader> sectionAndSort(@NonNull final List<Favorite> list) {\n        final List<Favorite> listCopy = new ArrayList<>(list);\n        Collections.sort(listCopy, (o1, o2) -> {\n            if (o1.getType() == o2.getType()) return 0;\n            // keep users at top\n            if (o1.getType() == FavoriteType.USER) return -1;\n            if (o2.getType() == FavoriteType.USER) return 1;\n            // keep locations at bottom\n            if (o1.getType() == FavoriteType.LOCATION) return 1;\n            if (o2.getType() == FavoriteType.LOCATION) return -1;\n            return 0;\n        });\n        final List<FavoriteModelOrHeader> modelOrHeaders = new ArrayList<>();\n        for (int i = 0; i < listCopy.size(); i++) {\n            final Favorite model = listCopy.get(i);\n            final FavoriteModelOrHeader prev = modelOrHeaders.isEmpty() ? null : modelOrHeaders.get(modelOrHeaders.size() - 1);\n            boolean prevWasSameType = prev != null && prev.model.getType() == model.getType();\n            if (prevWasSameType) {\n                // just add model\n                final FavoriteModelOrHeader modelOrHeader = new FavoriteModelOrHeader();\n                modelOrHeader.model = model;\n                modelOrHeaders.add(modelOrHeader);\n                continue;\n            }\n            // add header and model\n            FavoriteModelOrHeader modelOrHeader = new FavoriteModelOrHeader();\n            modelOrHeader.header = model.getType();\n            modelOrHeaders.add(modelOrHeader);\n            modelOrHeader = new FavoriteModelOrHeader();\n            modelOrHeader.model = model;\n            modelOrHeaders.add(modelOrHeader);\n        }\n        return modelOrHeaders;\n    }\n\n    private static class FavoriteModelOrHeader {\n        FavoriteType header;\n        Favorite model;\n\n        boolean isHeader() {\n            return header != null;\n        }\n    }\n\n    public interface OnFavoriteClickListener {\n        void onClick(final Favorite model);\n    }\n\n    public interface OnFavoriteLongClickListener {\n        boolean onLongClick(final Favorite model);\n    }\n\n    public static class FavSectionViewHolder extends RecyclerView.ViewHolder {\n        private final ItemFavSectionHeaderBinding binding;\n\n        public FavSectionViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(final FavoriteType header) {\n            if (header == null) return;\n            final int headerText;\n            switch (header) {\n                case USER:\n                    headerText = R.string.accounts;\n                    break;\n                case HASHTAG:\n                    headerText = R.string.hashtags;\n                    break;\n                case LOCATION:\n                    headerText = R.string.locations;\n                    break;\n                default:\n                    headerText = R.string.unknown;\n                    break;\n            }\n            binding.getRoot().setText(headerText);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;\nimport awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;\nimport awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;\nimport awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;\nimport awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;\nimport awais.instagrabber.databinding.ItemFeedGridBinding;\nimport awais.instagrabber.databinding.ItemFeedPhotoBinding;\nimport awais.instagrabber.databinding.ItemFeedSliderBinding;\nimport awais.instagrabber.databinding.ItemFeedVideoBinding;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Caption;\nimport awais.instagrabber.repositories.responses.Media;\n\npublic final class FeedAdapterV2 extends ListAdapter<Media, RecyclerView.ViewHolder> {\n    private static final String TAG = \"FeedAdapterV2\";\n\n    private final FeedItemCallback feedItemCallback;\n    private final SelectionModeCallback selectionModeCallback;\n    private final Set<Integer> selectedPositions = new HashSet<>();\n    private final Set<Media> selectedFeedModels = new HashSet<>();\n\n    private PostsLayoutPreferences layoutPreferences;\n    private boolean selectionModeActive = false;\n\n\n    private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {\n            return Objects.equals(oldItem.getPk(), newItem.getPk());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {\n            final Caption oldItemCaption = oldItem.getCaption();\n            final Caption newItemCaption = newItem.getCaption();\n            return Objects.equals(oldItem.getPk(), newItem.getPk())\n                    && Objects.equals(getCaptionText(oldItemCaption), getCaptionText(newItemCaption));\n        }\n\n        private String getCaptionText(final Caption caption) {\n            if (caption == null) return null;\n            return caption.getText();\n        }\n    };\n    private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {\n        @Override\n        public boolean onPostLongClick(final int position, final Media feedModel) {\n            if (!selectionModeActive) {\n                selectionModeActive = true;\n                notifyDataSetChanged();\n                if (selectionModeCallback != null) {\n                    selectionModeCallback.onSelectionStart();\n                }\n            }\n            selectedPositions.add(position);\n            selectedFeedModels.add(feedModel);\n            notifyItemChanged(position);\n            if (selectionModeCallback != null) {\n                selectionModeCallback.onSelectionChange(selectedFeedModels);\n            }\n            return true;\n        }\n\n        @Override\n        public void onPostClick(final int position, final Media feedModel) {\n            if (!selectionModeActive) return;\n            if (selectedPositions.contains(position)) {\n                selectedPositions.remove(position);\n                selectedFeedModels.remove(feedModel);\n            } else {\n                selectedPositions.add(position);\n                selectedFeedModels.add(feedModel);\n            }\n            notifyItemChanged(position);\n            if (selectionModeCallback != null) {\n                selectionModeCallback.onSelectionChange(selectedFeedModels);\n            }\n            if (selectedPositions.isEmpty()) {\n                selectionModeActive = false;\n                notifyDataSetChanged();\n                if (selectionModeCallback != null) {\n                    selectionModeCallback.onSelectionEnd();\n                }\n            }\n        }\n    };\n\n    public FeedAdapterV2(@NonNull final PostsLayoutPreferences layoutPreferences,\n                         final FeedItemCallback feedItemCallback,\n                         final SelectionModeCallback selectionModeCallback) {\n        super(DIFF_CALLBACK);\n        this.layoutPreferences = layoutPreferences;\n        this.feedItemCallback = feedItemCallback;\n        this.selectionModeCallback = selectionModeCallback;\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final Context context = parent.getContext();\n        final LayoutInflater layoutInflater = LayoutInflater.from(context);\n        switch (layoutPreferences.getType()) {\n            case LINEAR:\n                return getLinearViewHolder(parent, layoutInflater, viewType);\n            case GRID:\n            case STAGGERED_GRID:\n            default:\n                final ItemFeedGridBinding binding = ItemFeedGridBinding.inflate(layoutInflater, parent, false);\n                return new FeedGridItemViewHolder(binding);\n        }\n    }\n\n    @NonNull\n    private RecyclerView.ViewHolder getLinearViewHolder(@NonNull final ViewGroup parent,\n                                                        final LayoutInflater layoutInflater,\n                                                        final int viewType) {\n        switch (MediaItemType.valueOf(viewType)) {\n            case MEDIA_TYPE_VIDEO: {\n                final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);\n                return new FeedVideoViewHolder(binding, feedItemCallback);\n            }\n            case MEDIA_TYPE_SLIDER: {\n                final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);\n                return new FeedSliderViewHolder(binding, feedItemCallback);\n            }\n            case MEDIA_TYPE_IMAGE:\n            default: {\n                final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);\n                return new FeedPhotoViewHolder(binding, feedItemCallback);\n            }\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) {\n        final Media feedModel = getItem(position);\n        if (feedModel == null) return;\n        switch (layoutPreferences.getType()) {\n            case LINEAR:\n                ((FeedItemViewHolder) viewHolder).bind(feedModel);\n                break;\n            case GRID:\n            case STAGGERED_GRID:\n            default:\n                ((FeedGridItemViewHolder) viewHolder).bind(position,\n                                                           feedModel,\n                                                           layoutPreferences,\n                                                           feedItemCallback,\n                                                           adapterSelectionCallback,\n                                                           selectionModeActive,\n                                                           selectedPositions.contains(position));\n        }\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return getItem(position).getType().getId();\n    }\n\n    public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {\n        this.layoutPreferences = layoutPreferences;\n    }\n\n    public void endSelection() {\n        if (!selectionModeActive) return;\n        selectionModeActive = false;\n        selectedPositions.clear();\n        selectedFeedModels.clear();\n        notifyDataSetChanged();\n        if (selectionModeCallback != null) {\n            selectionModeCallback.onSelectionEnd();\n        }\n    }\n\n    // @Override\n    // public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {\n    //     super.onViewAttachedToWindow(holder);\n    //     // Log.d(TAG, \"attached holder: \" + holder);\n    //     if (!(holder instanceof FeedSliderViewHolder)) return;\n    //     final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;\n    //     feedSliderViewHolder.startPlayingVideo();\n    // }\n    //\n    // @Override\n    // public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {\n    //     super.onViewDetachedFromWindow(holder);\n    //     // Log.d(TAG, \"detached holder: \" + holder);\n    //     if (!(holder instanceof FeedSliderViewHolder)) return;\n    //     final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;\n    //     feedSliderViewHolder.stopPlayingVideo();\n    // }\n\n    public interface FeedItemCallback {\n        void onPostClick(final Media feedModel);\n\n        void onProfilePicClick(final Media feedModel);\n\n        void onNameClick(final Media feedModel);\n\n        void onLocationClick(final Media feedModel);\n\n        void onMentionClick(final String mention);\n\n        void onHashtagClick(final String hashtag);\n\n        void onCommentsClick(final Media feedModel);\n\n        void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation);\n\n        void onEmailClick(final String emailId);\n\n        void onURLClick(final String url);\n\n        void onSliderClick(Media feedModel, int position);\n    }\n\n    public interface AdapterSelectionCallback {\n        boolean onPostLongClick(final int position, Media feedModel);\n\n        void onPostClick(final int position, Media feedModel);\n    }\n\n    public interface SelectionModeCallback {\n        void onSelectionStart();\n\n        void onSelectionChange(final Set<Media> selectedFeedModels);\n\n        void onSelectionEnd();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.View;\n\nimport awais.instagrabber.repositories.responses.Media;\n\n\npublic class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {\n    @Override\n    public void onPostClick(final Media media) {}\n\n    @Override\n    public void onProfilePicClick(final Media media) {}\n\n    @Override\n    public void onNameClick(final Media media) {}\n\n    @Override\n    public void onLocationClick(final Media media) {}\n\n    @Override\n    public void onMentionClick(final String mention) {}\n\n    @Override\n    public void onHashtagClick(final String hashtag) {}\n\n    @Override\n    public void onCommentsClick(final Media media) {}\n\n    @Override\n    public void onDownloadClick(final Media media, final int childPosition, final View popupLocation) {}\n\n    @Override\n    public void onEmailClick(final String emailId) {}\n\n    @Override\n    public void onURLClick(final String url) {}\n\n    @Override\n    public void onSliderClick(final Media media, final int position) {}\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FeedStoriesAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport awais.instagrabber.adapters.viewholder.FeedStoryViewHolder;\nimport awais.instagrabber.databinding.ItemHighlightBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic final class FeedStoriesAdapter extends ListAdapter<Story, FeedStoryViewHolder> {\n    private final OnFeedStoryClickListener listener;\n\n    private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId()) && oldItem.getSeen() == newItem.getSeen();\n        }\n    };\n\n    public FeedStoriesAdapter(final OnFeedStoryClickListener listener) {\n        super(diffCallback);\n        this.listener = listener;\n    }\n\n    @NonNull\n    @Override\n    public FeedStoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemHighlightBinding binding = ItemHighlightBinding.inflate(layoutInflater, parent, false);\n        return new FeedStoryViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final FeedStoryViewHolder holder, final int position) {\n        final Story model = getItem(position);\n        holder.bind(model, position, listener);\n    }\n\n    public interface OnFeedStoryClickListener {\n        void onFeedStoryClick(Story model, int position);\n\n        void onFeedStoryLongClick(Story model, int position);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.widget.Filter;\nimport android.widget.Filterable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.adapters.viewholder.StoryListViewHolder;\nimport awais.instagrabber.databinding.ItemNotificationBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\nimport awais.instagrabber.utils.TextUtils;\n\npublic final class FeedStoriesListAdapter extends ListAdapter<Story, StoryListViewHolder> implements Filterable {\n    private final OnFeedStoryClickListener listener;\n    private List<Story> list;\n\n    private final Filter filter = new Filter() {\n        @NonNull\n        @Override\n        protected FilterResults performFiltering(final CharSequence filter) {\n            final String query = TextUtils.isEmpty(filter) ? null : filter.toString().toLowerCase();\n            List<Story> filteredList = list;\n            if (list != null && query != null) {\n                filteredList = list.stream()\n                                   .filter(feedStoryModel -> feedStoryModel.getUser()\n                                                                           .getUsername()\n                                                                           .toLowerCase()\n                                                                           .contains(query))\n                                   .collect(Collectors.toList());\n            }\n            final FilterResults filterResults = new FilterResults();\n            filterResults.count = filteredList != null ? filteredList.size() : 0;\n            filterResults.values = filteredList;\n            return filterResults;\n        }\n\n        @Override\n        protected void publishResults(final CharSequence constraint, final FilterResults results) {\n            //noinspection unchecked\n            submitList((List<Story>) results.values, true);\n        }\n    };\n\n    private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId()) && oldItem.getSeen() == newItem.getSeen();\n        }\n    };\n\n    public FeedStoriesListAdapter(final OnFeedStoryClickListener listener) {\n        super(diffCallback);\n        this.listener = listener;\n    }\n\n    @Override\n    public Filter getFilter() {\n        return filter;\n    }\n\n    private void submitList(@Nullable final List<Story> list, final boolean isFiltered) {\n        if (!isFiltered) {\n            this.list = list;\n        }\n        super.submitList(list);\n    }\n\n    @Override\n    public void submitList(final List<Story> list) {\n        submitList(list, false);\n    }\n\n    @NonNull\n    @Override\n    public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);\n        return new StoryListViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {\n        final Story model = getItem(position);\n        holder.bind(model, listener);\n    }\n\n    public interface OnFeedStoryClickListener {\n        void onFeedStoryClick(final Story model);\n\n        void onProfileClick(final String username);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FiltersAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.graphics.Bitmap;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport awais.instagrabber.adapters.viewholder.FilterViewHolder;\nimport awais.instagrabber.databinding.ItemFilterBinding;\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\n\npublic class FiltersAdapter extends ListAdapter<Filter<?>, FilterViewHolder> {\n\n    private static final DiffUtil.ItemCallback<Filter<?>> DIFF_CALLBACK = new DiffUtil.ItemCallback<Filter<?>>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {\n            return oldItem.getType().equals(newItem.getType());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {\n            return oldItem.getType().equals(newItem.getType());\n        }\n    };\n\n    private final Bitmap bitmap;\n    private final OnFilterClickListener onFilterClickListener;\n    private final Collection<GPUImageFilter> filters;\n    private final String originalKey;\n    private int selectedPosition = 0;\n\n    public FiltersAdapter(final Collection<GPUImageFilter> filters,\n                          final String originalKey,\n                          final Bitmap bitmap,\n                          final OnFilterClickListener onFilterClickListener) {\n        super(DIFF_CALLBACK);\n        this.filters = filters;\n        this.originalKey = originalKey;\n        this.bitmap = bitmap;\n        this.onFilterClickListener = onFilterClickListener;\n        setHasStableIds(true);\n    }\n\n    @NonNull\n    @Override\n    public FilterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemFilterBinding binding = ItemFilterBinding.inflate(layoutInflater, parent, false);\n        return new FilterViewHolder(binding, filters, onFilterClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final FilterViewHolder holder, final int position) {\n        holder.bind(position, originalKey, bitmap, getItem(position), selectedPosition == position);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return getItem(position).getLabel();\n    }\n\n    public void setSelected(final int position) {\n        final int prev = this.selectedPosition;\n        this.selectedPosition = position;\n        notifyItemChanged(position);\n        notifyItemChanged(prev);\n    }\n\n    public void setSelectedFilter(final GPUImageFilter instance) {\n        final List<Filter<?>> currentList = getCurrentList();\n        int index = -1;\n        for (int i = 0; i < currentList.size(); i++) {\n            final Filter<?> filter = currentList.get(i);\n            final GPUImageFilter filterInstance = filter.getInstance();\n            if (filterInstance.getClass() == instance.getClass()) {\n                index = i;\n                break;\n            }\n        }\n        if (index < 0) return;\n        setSelected(index);\n    }\n\n    public interface OnFilterClickListener {\n        void onClick(int position, Filter<?> filter);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/FollowAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Filter;\nimport android.widget.Filterable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.FollowsViewHolder;\nimport awais.instagrabber.databinding.ItemFollowBinding;\nimport awais.instagrabber.interfaces.OnGroupClickListener;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.TextUtils;\nimport thoughtbot.expandableadapter.ExpandableGroup;\nimport thoughtbot.expandableadapter.ExpandableList;\nimport thoughtbot.expandableadapter.ExpandableListPosition;\nimport thoughtbot.expandableadapter.GroupViewHolder;\n\n// thanks to ThoughtBot's ExpandableRecyclerViewAdapter\n//   https://github.com/thoughtbot/expandable-recycler-view\npublic final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnGroupClickListener, Filterable {\n    private final View.OnClickListener onClickListener;\n    private final ExpandableList expandableListOriginal;\n    private final boolean hasManyGroups;\n    private ExpandableList expandableList;\n\n    private final Filter filter = new Filter() {\n        @Nullable\n        @Override\n        protected FilterResults performFiltering(final CharSequence filter) {\n            final List<User> filteredItems = new ArrayList<User>();\n            if (expandableListOriginal.groups == null || TextUtils.isEmpty(filter)) return null;\n            final String query = filter.toString().toLowerCase();\n            final ArrayList<ExpandableGroup> groups = new ArrayList<ExpandableGroup>();\n            for (int x = 0; x < expandableListOriginal.groups.size(); ++x) {\n                final ExpandableGroup expandableGroup = expandableListOriginal.groups.get(x);\n                final String title = expandableGroup.getTitle();\n                final List<User> items = expandableGroup.getItems();\n                if (items != null) {\n                    final List<User> toReturn = items.stream()\n                            .filter(u -> hasKey(query, u.getUsername(), u.getFullName()))\n                            .collect(Collectors.toList());\n                    groups.add(new ExpandableGroup(title, toReturn));\n                }\n            }\n            final FilterResults filterResults = new FilterResults();\n            filterResults.values = new ExpandableList(groups, expandableList.expandedGroupIndexes);\n            return filterResults;\n        }\n\n        private boolean hasKey(final String key, final String username, final String name) {\n            if (TextUtils.isEmpty(key)) return true;\n            final boolean hasUserName = username != null && username.toLowerCase().contains(key);\n            if (!hasUserName && name != null) return name.toLowerCase().contains(key);\n            return true;\n        }\n\n        @Override\n        protected void publishResults(final CharSequence constraint, final FilterResults results) {\n            if (results == null) {\n                expandableList = expandableListOriginal;\n            }\n            else {\n                final ExpandableList filteredList = (ExpandableList) results.values;\n                expandableList = filteredList;\n            }\n            notifyDataSetChanged();\n        }\n    };\n\n    public FollowAdapter(final View.OnClickListener onClickListener, @NonNull final ArrayList<ExpandableGroup> groups) {\n        this.expandableListOriginal = new ExpandableList(groups);\n        expandableList = this.expandableListOriginal;\n        this.onClickListener = onClickListener;\n        this.hasManyGroups = groups.size() > 1;\n    }\n\n    @Override\n    public Filter getFilter() {\n        return filter;\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final boolean isGroup = hasManyGroups && viewType == ExpandableListPosition.GROUP;\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final View view;\n        if (isGroup) {\n            view = layoutInflater.inflate(R.layout.header_follow, parent, false);\n            return new GroupViewHolder(view, this);\n        } else {\n            final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);\n            return new FollowsViewHolder(binding);\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        final ExpandableListPosition listPos = expandableList.getUnflattenedPosition(position);\n        final ExpandableGroup group = expandableList.getExpandableGroup(listPos);\n\n        if (hasManyGroups && listPos.type == ExpandableListPosition.GROUP) {\n            final GroupViewHolder gvh = (GroupViewHolder) holder;\n            gvh.setTitle(group.getTitle());\n            gvh.toggle(isGroupExpanded(group));\n            return;\n        }\n        final User model = group.getItems().get(hasManyGroups ? listPos.childPos : position);\n        ((FollowsViewHolder) holder).bind(model, onClickListener);\n    }\n\n    @Override\n    public int getItemCount() {\n        return expandableList.getVisibleItemCount() - (hasManyGroups ? 0 : 1);\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return !hasManyGroups ? 0 : expandableList.getUnflattenedPosition(position).type;\n    }\n\n    @Override\n    public void toggleGroup(final int flatPos) {\n        final ExpandableListPosition listPosition = expandableList.getUnflattenedPosition(flatPos);\n\n        final int groupPos = listPosition.groupPos;\n        final int positionStart = expandableList.getFlattenedGroupIndex(listPosition) + 1;\n        final int positionEnd = expandableList.groups.get(groupPos).getItemCount();\n\n        final boolean isExpanded = expandableList.expandedGroupIndexes[groupPos];\n        expandableList.expandedGroupIndexes[groupPos] = !isExpanded;\n        notifyItemChanged(positionStart - 1);\n        if (positionEnd > 0) {\n            if (isExpanded) notifyItemRangeRemoved(positionStart, positionEnd);\n            else notifyItemRangeInserted(positionStart, positionEnd);\n        }\n    }\n\n    public boolean isGroupExpanded(final ExpandableGroup group) {\n        return expandableList.expandedGroupIndexes[expandableList.groups.indexOf(group)];\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/GifItemsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.net.Uri;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.imagepipeline.common.ResizeOptions;\nimport com.facebook.imagepipeline.image.ImageInfo;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.databinding.ItemMediaBinding;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif;\nimport awais.instagrabber.utils.Utils;\n\npublic class GifItemsAdapter extends ListAdapter<GiphyGif, GifItemsAdapter.GifViewHolder> {\n\n    private static final DiffUtil.ItemCallback<GiphyGif> diffCallback = new DiffUtil.ItemCallback<GiphyGif>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {\n            return Objects.equals(oldItem.getId(), newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {\n            return Objects.equals(oldItem.getId(), newItem.getId());\n        }\n    };\n\n    private final OnItemClickListener onItemClickListener;\n\n    public GifItemsAdapter(final OnItemClickListener onItemClickListener) {\n        super(diffCallback);\n        this.onItemClickListener = onItemClickListener;\n    }\n\n    @NonNull\n    @Override\n    public GifViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);\n        return new GifViewHolder(binding, onItemClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final GifViewHolder holder, final int position) {\n        holder.bind(getItem(position));\n    }\n\n    public static class GifViewHolder extends RecyclerView.ViewHolder {\n        private static final String TAG = GifViewHolder.class.getSimpleName();\n        private static final int size = Utils.displayMetrics.widthPixels / 3;\n\n        private final ItemMediaBinding binding;\n        private final OnItemClickListener onItemClickListener;\n\n        public GifViewHolder(@NonNull final ItemMediaBinding binding,\n                             final OnItemClickListener onItemClickListener) {\n            super(binding.getRoot());\n            this.binding = binding;\n            this.onItemClickListener = onItemClickListener;\n            binding.duration.setVisibility(View.GONE);\n            final GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(itemView.getResources());\n            builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);\n            binding.item.setHierarchy(builder.build());\n        }\n\n        public void bind(final GiphyGif item) {\n            if (onItemClickListener != null) {\n                itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));\n            }\n            final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {\n                @Override\n                public void onFailure(final String id, final Throwable throwable) {\n                    Log.e(TAG, \"onFailure: \", throwable);\n                }\n            };\n            final ImageRequest request = ImageRequestBuilder\n                    .newBuilderWithSource(Uri.parse(item.getImages().getFixedHeight().getWebp()))\n                    .setResizeOptions(ResizeOptions.forDimensions(size, size))\n                    .build();\n            final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()\n                                                                  .setImageRequest(request)\n                                                                  .setAutoPlayAnimations(true)\n                                                                  .setControllerListener(controllerListener);\n            binding.item.setController(builder.build());\n        }\n    }\n\n    public interface OnItemClickListener {\n        void onItemClick(GiphyGif giphyGif);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport awais.instagrabber.adapters.viewholder.StoryListViewHolder;\nimport awais.instagrabber.databinding.ItemNotificationBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic final class HighlightStoriesListAdapter extends ListAdapter<Story, StoryListViewHolder> {\n    private final OnHighlightStoryClickListener listener;\n\n    private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n    };\n\n    public HighlightStoriesListAdapter(final OnHighlightStoryClickListener listener) {\n        super(diffCallback);\n        this.listener = listener;\n    }\n\n    @NonNull\n    @Override\n    public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);\n        return new StoryListViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {\n        final Story model = getItem(position);\n        holder.bind(model, position, listener);\n    }\n\n    public interface OnHighlightStoryClickListener {\n        void onHighlightClick(final Story model, final int position);\n\n        void onProfileClick(final String username);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/HighlightsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport awais.instagrabber.adapters.viewholder.HighlightViewHolder;\nimport awais.instagrabber.databinding.ItemHighlightBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic final class HighlightsAdapter extends ListAdapter<Story, HighlightViewHolder> {\n\n    private final OnHighlightClickListener clickListener;\n\n    private static final DiffUtil.ItemCallback<Story> diffCallback = new DiffUtil.ItemCallback<Story>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Story oldItem, @NonNull final Story newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n    };\n\n    public HighlightsAdapter(final OnHighlightClickListener clickListener) {\n        super(diffCallback);\n        this.clickListener = clickListener;\n    }\n\n    @NonNull\n    @Override\n    public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemHighlightBinding binding = ItemHighlightBinding.inflate(layoutInflater, parent, false);\n        return new HighlightViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) {\n        final Story highlightModel = getItem(position);\n        if (clickListener != null) {\n            holder.itemView.setOnClickListener(v -> clickListener.onHighlightClick(highlightModel, position));\n        }\n        holder.bind(highlightModel);\n    }\n\n    public interface OnHighlightClickListener {\n        void onHighlightClick(final Story model, final int position);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/KeywordsFilterAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.dialogs.KeywordsFilterDialogViewHolder;\n\npublic class KeywordsFilterAdapter extends RecyclerView.Adapter<KeywordsFilterDialogViewHolder> {\n\n    private final Context context;\n    private final ArrayList<String> items;\n\n    public KeywordsFilterAdapter(Context context, ArrayList<String> items){\n        this.context = context;\n        this.items = items;\n    }\n\n    @NonNull\n    @Override\n    public KeywordsFilterDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        final View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_keyword, parent, false);\n        return new KeywordsFilterDialogViewHolder(v);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull KeywordsFilterDialogViewHolder holder, int position) {\n        holder.bind(items, position, context, this);\n    }\n\n    @Override\n    public int getItemCount() {\n        return items.size();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.List;\n\nimport awais.instagrabber.adapters.viewholder.FollowsViewHolder;\nimport awais.instagrabber.databinding.ItemFollowBinding;\nimport awais.instagrabber.repositories.responses.User;\n\npublic final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> {\n    private final List<User> profileModels;\n    private final View.OnClickListener onClickListener;\n\n    public LikesAdapter(final List<User> profileModels,\n                        final View.OnClickListener onClickListener) {\n        this.profileModels = profileModels;\n        this.onClickListener = onClickListener;\n    }\n\n    @NonNull\n    @Override\n    public FollowsViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);\n        return new FollowsViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {\n        final User model = profileModels.get(position);\n        holder.bind(model, onClickListener);\n    }\n\n    @Override\n    public int getItemCount() {\n        return profileModels.size();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.adapters.viewholder.NotificationViewHolder;\nimport awais.instagrabber.databinding.ItemNotificationBinding;\nimport awais.instagrabber.models.enums.NotificationType;\nimport awais.instagrabber.repositories.responses.notification.Notification;\n\npublic final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> {\n    private final OnNotificationClickListener notificationClickListener;\n\n    private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {\n        @Override\n        public boolean areItemsTheSame(final Notification oldItem, final Notification newItem) {\n            return Objects.requireNonNull(oldItem.getPk()).equals(newItem.getPk());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {\n            return Objects.requireNonNull(oldItem.getPk()).equals(newItem.getPk()) && Objects.equals(oldItem.getType(), newItem.getType());\n        }\n    };\n\n    public NotificationsAdapter(final OnNotificationClickListener notificationClickListener) {\n        super(DIFF_CALLBACK);\n        this.notificationClickListener = notificationClickListener;\n    }\n\n    @NonNull\n    @Override\n    public NotificationViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);\n        return new NotificationViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {\n        final Notification Notification = getItem(position);\n        holder.bind(Notification, notificationClickListener);\n    }\n\n    @Override\n    public void submitList(@Nullable final List<Notification> list, @Nullable final Runnable commitCallback) {\n        if (list == null) {\n            super.submitList(null, commitCallback);\n            return;\n        }\n        super.submitList(sort(list), commitCallback);\n    }\n\n    @Override\n    public void submitList(@Nullable final List<Notification> list) {\n        if (list == null) {\n            super.submitList(null);\n            return;\n        }\n        super.submitList(sort(list));\n    }\n\n    private List<Notification> sort(final List<Notification> list) {\n        final List<Notification> listCopy = new ArrayList<>(list).stream()\n                                                                 .filter(i -> i.getType() != null)\n                                                                 .collect(Collectors.toList());\n        Collections.sort(listCopy, (o1, o2) -> {\n            // keep requests at top\n            if (o1.getType() == o2.getType()\n                    && o1.getType() == NotificationType.REQUEST\n                    && o2.getType() == NotificationType.REQUEST) return 0;\n            else if (o1.getType() == NotificationType.REQUEST) return -1;\n            else if (o2.getType() == NotificationType.REQUEST) return 1;\n            // timestamp\n            return Double.compare(o2.getArgs().getTimestamp(), o1.getArgs().getTimestamp());\n        });\n        return listCopy;\n    }\n\n    public interface OnNotificationClickListener {\n        void onNotificationClick(final Notification model);\n\n        void onProfileClick(final String username);\n\n        void onPreviewClick(final Notification model);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/SavedCollectionsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;\nimport awais.instagrabber.databinding.ItemDiscoverTopicBinding;\nimport awais.instagrabber.repositories.responses.saved.SavedCollection;\n\npublic class SavedCollectionsAdapter extends ListAdapter<SavedCollection, TopicClusterViewHolder> {\n    private static final DiffUtil.ItemCallback<SavedCollection> DIFF_CALLBACK = new DiffUtil.ItemCallback<SavedCollection>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {\n            return oldItem.getCollectionId().equals(newItem.getCollectionId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {\n            if (oldItem.getCoverMediaList() != null && newItem.getCoverMediaList() != null\n                && oldItem.getCoverMediaList().size() == newItem.getCoverMediaList().size()) {\n                return Objects.equals(oldItem.getCoverMediaList().get(0).getId(), newItem.getCoverMediaList().get(0).getId());\n            }\n            else if (oldItem.getCoverMedia() != null && newItem.getCoverMedia() != null) {\n                return Objects.equals(oldItem.getCoverMedia().getId(), newItem.getCoverMedia().getId());\n            }\n            return false;\n        }\n    };\n\n    private final OnCollectionClickListener onCollectionClickListener;\n\n    public SavedCollectionsAdapter(final OnCollectionClickListener onCollectionClickListener) {\n        super(DIFF_CALLBACK);\n        this.onCollectionClickListener = onCollectionClickListener;\n    }\n\n    @NonNull\n    @Override\n    public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);\n        return new TopicClusterViewHolder(binding, null, onCollectionClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {\n        final SavedCollection topicCluster = getItem(position);\n        holder.bind(topicCluster);\n    }\n\n    public interface OnCollectionClickListener {\n        void onCollectionClick(SavedCollection savedCollection, View root, View cover, View title, int titleColor, int backgroundColor);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/SearchCategoryAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.Fragment;\nimport androidx.viewpager2.adapter.FragmentStateAdapter;\n\nimport java.util.List;\n\nimport awais.instagrabber.fragments.search.SearchCategoryFragment;\nimport awais.instagrabber.models.enums.FavoriteType;\n\npublic class SearchCategoryAdapter extends FragmentStateAdapter {\n\n    private final List<FavoriteType> categories;\n\n    public SearchCategoryAdapter(@NonNull final Fragment fragment,\n                                 @NonNull final List<FavoriteType> categories) {\n        super(fragment);\n        this.categories = categories;\n\n    }\n\n    @NonNull\n    @Override\n    public Fragment createFragment(final int position) {\n        return SearchCategoryFragment.newInstance(categories.get(position));\n    }\n\n    @Override\n    public int getItemCount() {\n        return categories.size();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/SearchItemsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.AdapterListUpdateCallback;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.AsyncListDiffer;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.SearchItemViewHolder;\nimport awais.instagrabber.databinding.ItemFavSectionHeaderBinding;\nimport awais.instagrabber.databinding.ItemSearchResultBinding;\nimport awais.instagrabber.fragments.search.SearchCategoryFragment.OnSearchItemClickListener;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.search.SearchItem;\n\npublic final class SearchItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {\n    private static final String TAG = SearchItemsAdapter.class.getSimpleName();\n    private static final DiffUtil.ItemCallback<SearchItemOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<SearchItemOrHeader>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final SearchItemOrHeader oldItem, @NonNull final SearchItemOrHeader newItem) {\n            return Objects.equals(oldItem, newItem);\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final SearchItemOrHeader oldItem, @NonNull final SearchItemOrHeader newItem) {\n            return Objects.equals(oldItem, newItem);\n        }\n    };\n    private static final String RECENT = \"recent\";\n    private static final String FAVORITE = \"favorite\";\n    private static final int VIEW_TYPE_HEADER = 0;\n    private static final int VIEW_TYPE_ITEM = 1;\n\n    private final OnSearchItemClickListener onSearchItemClickListener;\n    private final AsyncListDiffer<SearchItemOrHeader> differ;\n\n    public SearchItemsAdapter(final OnSearchItemClickListener onSearchItemClickListener) {\n        differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),\n                                       new AsyncDifferConfig.Builder<>(DIFF_CALLBACK).build());\n        this.onSearchItemClickListener = onSearchItemClickListener;\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        if (viewType == VIEW_TYPE_HEADER) {\n            return new HeaderViewHolder(ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false));\n        }\n        final ItemSearchResultBinding binding = ItemSearchResultBinding.inflate(layoutInflater, parent, false);\n        return new SearchItemViewHolder(binding, onSearchItemClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        if (getItemViewType(position) == VIEW_TYPE_HEADER) {\n            final SearchItemOrHeader searchItemOrHeader = getItem(position);\n            if (!searchItemOrHeader.isHeader()) return;\n            ((HeaderViewHolder) holder).bind(searchItemOrHeader.header);\n            return;\n        }\n        ((SearchItemViewHolder) holder).bind(getItem(position).searchItem);\n    }\n\n    protected SearchItemOrHeader getItem(int position) {\n        return differ.getCurrentList().get(position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return getItem(position).isHeader() ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM;\n    }\n\n    public void submitList(@Nullable final List<SearchItem> list) {\n        if (list == null) {\n            differ.submitList(null);\n            return;\n        }\n        differ.submitList(sectionAndSort(list));\n    }\n\n    public void submitList(@Nullable final List<SearchItem> list, @Nullable final Runnable commitCallback) {\n        if (list == null) {\n            differ.submitList(null, commitCallback);\n            return;\n        }\n        differ.submitList(sectionAndSort(list), commitCallback);\n    }\n\n    @NonNull\n    private List<SearchItemOrHeader> sectionAndSort(@NonNull final List<SearchItem> list) {\n        final boolean containsRecentOrFavorite = list.stream().anyMatch(searchItem -> searchItem.isRecent() || searchItem.isFavorite());\n        // Don't do anything if not showing recent results\n        if (!containsRecentOrFavorite) {\n            return list.stream()\n                       .map(SearchItemOrHeader::new)\n                       .collect(Collectors.toList());\n        }\n        final List<SearchItem> listCopy = new ArrayList<>(list);\n        Collections.sort(listCopy, (o1, o2) -> {\n            final boolean bothRecent = o1.isRecent() && o2.isRecent();\n            if (bothRecent) {\n                // Don't sort\n                return 0;\n            }\n            final boolean bothFavorite = o1.isFavorite() && o2.isFavorite();\n            if (bothFavorite) {\n                if (o1.getType() == o2.getType()) return 0;\n                // keep users at top\n                if (o1.getType() == FavoriteType.USER) return -1;\n                if (o2.getType() == FavoriteType.USER) return 1;\n                // keep locations at bottom\n                if (o1.getType() == FavoriteType.LOCATION) return 1;\n                if (o2.getType() == FavoriteType.LOCATION) return -1;\n            }\n            // keep recents at top\n            if (o1.isRecent()) return -1;\n            if (o2.isRecent()) return 1;\n            return 0;\n        });\n        final List<SearchItemOrHeader> itemOrHeaders = new ArrayList<>();\n        for (int i = 0; i < listCopy.size(); i++) {\n            final SearchItem searchItem = listCopy.get(i);\n            final SearchItemOrHeader prev = itemOrHeaders.isEmpty() ? null : itemOrHeaders.get(itemOrHeaders.size() - 1);\n            boolean prevWasSameType = prev != null && ((prev.searchItem.isRecent() && searchItem.isRecent())\n                    || (prev.searchItem.isFavorite() && searchItem.isFavorite()));\n            if (prevWasSameType) {\n                // just add the item\n                itemOrHeaders.add(new SearchItemOrHeader(searchItem));\n                continue;\n            }\n            // add header and item\n            // add header only if search item is recent or favorite\n            if (searchItem.isRecent() || searchItem.isFavorite()) {\n                itemOrHeaders.add(new SearchItemOrHeader(searchItem.isRecent() ? RECENT : FAVORITE));\n            }\n            itemOrHeaders.add(new SearchItemOrHeader(searchItem));\n        }\n        return itemOrHeaders;\n    }\n\n    private static class SearchItemOrHeader {\n        String header;\n        SearchItem searchItem;\n\n        public SearchItemOrHeader(final SearchItem searchItem) {\n            this.searchItem = searchItem;\n        }\n\n        public SearchItemOrHeader(final String header) {\n            this.header = header;\n        }\n\n        boolean isHeader() {\n            return header != null;\n        }\n\n        @Override\n        public boolean equals(final Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            final SearchItemOrHeader that = (SearchItemOrHeader) o;\n            return Objects.equals(header, that.header) &&\n                    Objects.equals(searchItem, that.searchItem);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(header, searchItem);\n        }\n    }\n\n    public static class HeaderViewHolder extends RecyclerView.ViewHolder {\n        private final ItemFavSectionHeaderBinding binding;\n\n        public HeaderViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(final String header) {\n            if (header == null) return;\n            final int headerText;\n            switch (header) {\n                case RECENT:\n                    headerText = R.string.recent;\n                    break;\n                case FAVORITE:\n                    headerText = R.string.title_favorites;\n                    break;\n                default:\n                    headerText = R.string.unknown;\n                    break;\n            }\n            binding.getRoot().setText(headerText);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/SliderCallbackAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.View;\n\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\n\nimport awais.instagrabber.repositories.responses.Media;\n\npublic class SliderCallbackAdapter implements SliderItemsAdapter.SliderCallback {\n    @Override\n    public void onThumbnailLoaded(final int position) {}\n\n    @Override\n    public void onItemClicked(final int position, final Media media, final View view) {}\n\n    @Override\n    public void onPlayerPlay(final int position) {}\n\n    @Override\n    public void onPlayerPause(final int position) {}\n\n    @Override\n    public void onPlayerRelease(final int position) {}\n\n    @Override\n    public void onFullScreenModeChanged(final boolean isFullScreen, final StyledPlayerView playerView) {}\n\n    @Override\n    public boolean isInFullScreen() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\n\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\n\nimport awais.instagrabber.adapters.viewholder.SliderItemViewHolder;\nimport awais.instagrabber.adapters.viewholder.SliderPhotoViewHolder;\nimport awais.instagrabber.adapters.viewholder.SliderVideoViewHolder;\nimport awais.instagrabber.databinding.ItemSliderPhotoBinding;\nimport awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\n\npublic final class SliderItemsAdapter extends ListAdapter<Media, SliderItemViewHolder> {\n\n    private final boolean loadVideoOnItemClick;\n    private final SliderCallback sliderCallback;\n\n    private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {\n            return oldItem.getPk().equals(newItem.getPk());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {\n            return oldItem.getPk().equals(newItem.getPk());\n        }\n    };\n\n    public SliderItemsAdapter(final boolean loadVideoOnItemClick,\n                              final SliderCallback sliderCallback) {\n        super(DIFF_CALLBACK);\n        this.loadVideoOnItemClick = loadVideoOnItemClick;\n        this.sliderCallback = sliderCallback;\n    }\n\n    @NonNull\n    @Override\n    public SliderItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());\n        final MediaItemType mediaItemType = MediaItemType.valueOf(viewType);\n        switch (mediaItemType) {\n            case MEDIA_TYPE_VIDEO: {\n                final LayoutVideoPlayerWithThumbnailBinding binding = LayoutVideoPlayerWithThumbnailBinding.inflate(inflater, parent, false);\n                return new SliderVideoViewHolder(binding, loadVideoOnItemClick);\n            }\n            case MEDIA_TYPE_IMAGE:\n            default:\n                final ItemSliderPhotoBinding binding = ItemSliderPhotoBinding.inflate(inflater, parent, false);\n                return new SliderPhotoViewHolder(binding);\n        }\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {\n        final Media media = getItem(position);\n        holder.bind(media, position, sliderCallback);\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        final Media media = getItem(position);\n        return media.getType().getId();\n    }\n\n    // @NonNull\n    // @Override\n    // public Object instantiateItem(@NonNull final ViewGroup container, final int position) {\n    //     final Context context = container.getContext();\n    //     final ViewerPostModel sliderItem = sliderItems.get(position);\n    //\n    //     if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {\n    //         final ViewSwitcher viewSwitcher = createViewSwitcher(context, position, sliderItem.getThumbnailUrl(), sliderItem.getDisplayUrl());\n    //         container.addView(viewSwitcher);\n    //         return viewSwitcher;\n    //     }\n    //     final GenericDraweeHierarchy hierarchy = GenericDraweeHierarchyBuilder.newInstance(container.getResources())\n    //                                                                           .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)\n    //                                                                           .build();\n    //     final SimpleDraweeView photoView = new SimpleDraweeView(context, hierarchy);\n    //     photoView.setLayoutParams(layoutParams);\n    //     final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(sliderItem.getDisplayUrl()))\n    //                                                          .setLocalThumbnailPreviewsEnabled(true)\n    //                                                          .setProgressiveRenderingEnabled(true)\n    //                                                          .build();\n    //     photoView.setImageRequest(imageRequest);\n    //     container.addView(photoView);\n    //     return photoView;\n    // }\n\n    // @NonNull\n    // private ViewSwitcher createViewSwitcher(final Context context,\n    //                                         final int position,\n    //                                         final String thumbnailUrl,\n    //                                         final String displayUrl) {\n    //\n    //     final ViewSwitcher viewSwitcher = new ViewSwitcher(context);\n    //     viewSwitcher.setLayoutParams(layoutParams);\n    //\n    //     final FrameLayout frameLayout = new FrameLayout(context);\n    //     frameLayout.setLayoutParams(layoutParams);\n    //\n    //     final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(context.getResources())\n    //             .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)\n    //             .build();\n    //     final SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context, hierarchy);\n    //     simpleDraweeView.setLayoutParams(layoutParams);\n    //     simpleDraweeView.setImageURI(thumbnailUrl);\n    //     frameLayout.addView(simpleDraweeView);\n    //\n    //     final AppCompatImageView imageView = new AppCompatImageView(context);\n    //     final int px = Utils.convertDpToPx(50);\n    //     final FrameLayout.LayoutParams playButtonLayoutParams = new FrameLayout.LayoutParams(px, px);\n    //     playButtonLayoutParams.gravity = Gravity.CENTER;\n    //     imageView.setLayoutParams(playButtonLayoutParams);\n    //     imageView.setImageResource(R.drawable.exo_icon_play);\n    //     frameLayout.addView(imageView);\n    //\n    //     viewSwitcher.addView(frameLayout);\n    //\n    //     final PlayerView playerView = new PlayerView(context);\n    //     viewSwitcher.addView(playerView);\n    //     if (shouldAutoPlay && position == 0) {\n    //         loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener);\n    //     } else\n    //         frameLayout.setOnClickListener(v -> loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener));\n    //     return viewSwitcher;\n    // }\n\n    public interface SliderCallback {\n        void onThumbnailLoaded(int position);\n\n        void onItemClicked(int position, final Media media, final View view);\n\n        void onPlayerPlay(int position);\n\n        void onPlayerPause(int position);\n\n        void onPlayerRelease(int position);\n\n        void onFullScreenModeChanged(boolean isFullScreen, final StyledPlayerView playerView);\n\n        boolean isInFullScreen();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/StoriesAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport java.util.List;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.databinding.ItemStoryBinding;\nimport awais.instagrabber.repositories.responses.stories.StoryMedia;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic final class StoriesAdapter extends ListAdapter<StoryMedia, StoriesAdapter.StoryViewHolder> {\n    private final OnItemClickListener onItemClickListener;\n\n    private static final DiffUtil.ItemCallback<StoryMedia> diffCallback = new DiffUtil.ItemCallback<StoryMedia>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final StoryMedia oldItem, @NonNull final StoryMedia newItem) {\n            return oldItem.getId().equals(newItem.getId());\n        }\n    };\n\n    public StoriesAdapter(final OnItemClickListener onItemClickListener) {\n        super(diffCallback);\n        this.onItemClickListener = onItemClickListener;\n    }\n\n    @NonNull\n    @Override\n    public StoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemStoryBinding binding = ItemStoryBinding.inflate(layoutInflater, parent, false);\n        return new StoryViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) {\n        final StoryMedia storyMedia = getItem(position);\n        holder.bind(storyMedia, position, onItemClickListener);\n    }\n\n    public final static class StoryViewHolder extends RecyclerView.ViewHolder {\n        private final ItemStoryBinding binding;\n\n        public StoryViewHolder(final ItemStoryBinding binding) {\n            super(binding.getRoot());\n            this.binding = binding;\n        }\n\n        public void bind(final StoryMedia model,\n                         final int position,\n                         final OnItemClickListener clickListener) {\n            if (model == null) return;\n            model.setPosition(position);\n\n            itemView.setTag(model);\n            itemView.setOnClickListener(v -> {\n                if (clickListener == null) return;\n                clickListener.onItemClick(model, position);\n            });\n\n            binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);\n            binding.icon.setImageURI(ResponseBodyUtils.getThumbUrl(model));\n        }\n    }\n\n    public void paginate(final int newIndex) {\n        final List<StoryMedia> list = getCurrentList();\n        for (int i = 0; i < list.size(); i++) {\n            final StoryMedia item = list.get(i);\n            if (!item.isCurrentSlide() && i != newIndex) continue;\n            item.setCurrentSlide(i == newIndex);\n            notifyItemChanged(i, item);\n        }\n    }\n\n    public interface OnItemClickListener {\n        void onItemClick(StoryMedia storyModel, int position);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/TabsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.TabViewHolder;\nimport awais.instagrabber.databinding.ItemFavSectionHeaderBinding;\nimport awais.instagrabber.databinding.ItemTabOrderPrefBinding;\nimport awais.instagrabber.models.Tab;\nimport awais.instagrabber.utils.Utils;\n\npublic class TabsAdapter extends ListAdapter<TabsAdapter.TabOrHeader, RecyclerView.ViewHolder> {\n    private static final DiffUtil.ItemCallback<TabOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<TabOrHeader>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final TabOrHeader oldItem, @NonNull final TabOrHeader newItem) {\n            if (oldItem.isHeader() && newItem.isHeader()) {\n                return oldItem.header == newItem.header;\n            }\n            if (!oldItem.isHeader() && !newItem.isHeader()) {\n                final Tab oldTab = oldItem.tab;\n                final Tab newTab = newItem.tab;\n                return oldTab.getIconResId() == newTab.getIconResId()\n                        && Objects.equals(oldTab.getTitle(), newTab.getTitle());\n            }\n            return false;\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final TabOrHeader oldItem, @NonNull final TabOrHeader newItem) {\n            if (oldItem.isHeader() && newItem.isHeader()) {\n                return oldItem.header == newItem.header;\n            }\n            if (!oldItem.isHeader() && !newItem.isHeader()) {\n                final Tab oldTab = oldItem.tab;\n                final Tab newTab = newItem.tab;\n                return oldTab.getIconResId() == newTab.getIconResId()\n                        && Objects.equals(oldTab.getTitle(), newTab.getTitle());\n            }\n            return false;\n        }\n    };\n\n    private final TabAdapterCallback tabAdapterCallback;\n\n    private List<Tab> current = new ArrayList<>();\n    private List<Tab> others = new ArrayList<>();\n\n    public TabsAdapter(@NonNull final TabAdapterCallback tabAdapterCallback) {\n        super(DIFF_CALLBACK);\n        this.tabAdapterCallback = tabAdapterCallback;\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        if (viewType == 1) {\n            final ItemTabOrderPrefBinding binding = ItemTabOrderPrefBinding.inflate(layoutInflater, parent, false);\n            return new TabViewHolder(binding, tabAdapterCallback);\n        }\n        final ItemFavSectionHeaderBinding headerBinding = ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false);\n        return new DirectUsersAdapter.HeaderViewHolder(headerBinding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        if (holder instanceof DirectUsersAdapter.HeaderViewHolder) {\n            ((DirectUsersAdapter.HeaderViewHolder) holder).bind(R.string.other_tabs);\n            return;\n        }\n        if (holder instanceof TabViewHolder) {\n            final Tab tab = getItem(position).tab;\n            ((TabViewHolder) holder).bind(tab, others.contains(tab), current.size() == 5);\n        }\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return getItem(position).isHeader() ? 0 : 1;\n    }\n\n    public void submitList(final List<Tab> current, final List<Tab> others, final Runnable commitCallback) {\n        final ImmutableList.Builder<TabOrHeader> builder = ImmutableList.builder();\n        if (current != null) {\n            builder.addAll(current.stream()\n                                  .map(TabOrHeader::new)\n                                  .collect(Collectors.toList()));\n        }\n        builder.add(new TabOrHeader(R.string.other_tabs));\n        if (others != null) {\n            builder.addAll(others.stream()\n                                 .map(TabOrHeader::new)\n                                 .collect(Collectors.toList()));\n        }\n        // Mutable non-null copies\n        this.current = current != null ? new ArrayList<>(current) : new ArrayList<>();\n        this.others = others != null ? new ArrayList<>(others) : new ArrayList<>();\n        submitList(builder.build(), commitCallback);\n    }\n\n    public void submitList(final List<Tab> current, final List<Tab> others) {\n        submitList(current, others, null);\n    }\n\n    public void moveItem(final int from, final int to) {\n        final List<Tab> currentCopy = new ArrayList<>(current);\n        Utils.moveItem(from, to, currentCopy);\n        submitList(currentCopy, others);\n        tabAdapterCallback.onOrderChange(currentCopy);\n    }\n\n    public int getCurrentCount() {\n        return current.size();\n    }\n\n    public static class TabOrHeader {\n        Tab tab;\n        int header;\n\n        public TabOrHeader(final Tab tab) {\n            this.tab = tab;\n        }\n\n        public TabOrHeader(@StringRes final int header) {\n            this.header = header;\n        }\n\n        boolean isHeader() {\n            return header != 0;\n        }\n    }\n\n    public interface TabAdapterCallback {\n        void onStartDrag(TabViewHolder viewHolder);\n\n        void onOrderChange(List<Tab> newOrderTabs);\n\n        void onAdd(Tab tab);\n\n        void onRemove(Tab tab);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/UserSearchResultsAdapter.java",
    "content": "package awais.instagrabber.adapters;\n\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.ListAdapter;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;\nimport awais.instagrabber.adapters.viewholder.directmessages.RecipientThreadViewHolder;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient;\n\npublic final class UserSearchResultsAdapter extends ListAdapter<RankedRecipient, RecyclerView.ViewHolder> {\n    private static final int VIEW_TYPE_USER = 0;\n    private static final int VIEW_TYPE_THREAD = 1;\n    private static final DiffUtil.ItemCallback<RankedRecipient> DIFF_CALLBACK = new DiffUtil.ItemCallback<RankedRecipient>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {\n            final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;\n            if (!bothUsers) return false;\n            final boolean bothThreads = oldItem.getThread() != null && newItem.getThread() != null;\n            if (!bothThreads) return false;\n            if (bothUsers) {\n                return oldItem.getUser().getPk() == newItem.getUser().getPk();\n            }\n            return Objects.equals(oldItem.getThread().getThreadId(), newItem.getThread().getThreadId());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {\n            final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;\n            if (bothUsers) {\n                return Objects.equals(oldItem.getUser().getUsername(), newItem.getUser().getUsername()) &&\n                        Objects.equals(oldItem.getUser().getFullName(), newItem.getUser().getFullName());\n            }\n            return Objects.equals(oldItem.getThread().getThreadTitle(), newItem.getThread().getThreadTitle());\n        }\n    };\n\n    private final boolean showSelection;\n    private final Set<RankedRecipient> selectedRecipients;\n    private final OnDirectUserClickListener onUserClickListener;\n    private final OnRecipientClickListener onRecipientClickListener;\n\n    public UserSearchResultsAdapter(final boolean showSelection,\n                                    final OnRecipientClickListener onRecipientClickListener) {\n        super(DIFF_CALLBACK);\n        this.showSelection = showSelection;\n        selectedRecipients = showSelection ? new HashSet<>() : null;\n        this.onRecipientClickListener = onRecipientClickListener;\n        this.onUserClickListener = (position, user, selected) -> {\n            if (onRecipientClickListener != null) {\n                onRecipientClickListener.onClick(position, RankedRecipient.of(user), selected);\n            }\n        };\n        setHasStableIds(true);\n    }\n\n    @NonNull\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);\n        if (viewType == VIEW_TYPE_USER) {\n            return new DirectUserViewHolder(binding, onUserClickListener, null);\n        }\n        return new RecipientThreadViewHolder(binding, onRecipientClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {\n        final RankedRecipient recipient = getItem(position);\n        final int itemViewType = getItemViewType(position);\n        if (itemViewType == VIEW_TYPE_USER) {\n            boolean isSelected = false;\n            if (selectedRecipients != null) {\n                isSelected = selectedRecipients.stream()\n                                               .anyMatch(rankedRecipient -> rankedRecipient.getUser() != null\n                                                       && rankedRecipient.getUser().getPk() == recipient.getUser().getPk());\n            }\n            ((DirectUserViewHolder) holder).bind(position, recipient.getUser(), false, false, showSelection, isSelected);\n            return;\n        }\n        boolean isSelected = false;\n        if (selectedRecipients != null) {\n            isSelected = selectedRecipients.stream()\n                                           .anyMatch(rankedRecipient -> rankedRecipient.getThread() != null\n                                                   && Objects.equals(rankedRecipient.getThread().getThreadId(), recipient.getThread().getThreadId()));\n        }\n        ((RecipientThreadViewHolder) holder).bind(position, recipient.getThread(), showSelection, isSelected);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        final RankedRecipient recipient = getItem(position);\n        if (recipient.getUser() != null) {\n            return recipient.getUser().getPk();\n        }\n        if (recipient.getThread() != null) {\n            return recipient.getThread().getThreadTitle().hashCode();\n        }\n        return 0;\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        final RankedRecipient recipient = getItem(position);\n        return recipient.getUser() != null ? VIEW_TYPE_USER : VIEW_TYPE_THREAD;\n    }\n\n    public void setSelectedRecipient(final RankedRecipient recipient, final boolean selected) {\n        if (selectedRecipients == null || recipient == null || (recipient.getUser() == null && recipient.getThread() == null)) return;\n        final boolean isUser = recipient.getUser() != null;\n        int position = -1;\n        final List<RankedRecipient> currentList = getCurrentList();\n        for (int i = 0; i < currentList.size(); i++) {\n            final RankedRecipient temp = currentList.get(i);\n            if (isUser) {\n                if (temp.getUser() != null && temp.getUser().getPk() == recipient.getUser().getPk()) {\n                    position = i;\n                    break;\n                }\n                continue;\n            }\n            if (temp.getThread() != null && Objects.equals(temp.getThread().getThreadId(), recipient.getThread().getThreadId())) {\n                position = i;\n                break;\n            }\n        }\n        if (position < 0) return;\n        if (selected) {\n            selectedRecipients.add(recipient);\n        } else {\n            selectedRecipients.remove(recipient);\n        }\n        notifyItemChanged(position);\n    }\n\n    public interface OnRecipientClickListener {\n        void onClick(int position, RankedRecipient recipient, final boolean isSelected);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.util.TypedValue;\nimport android.view.Menu;\nimport android.view.View;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.view.ContextThemeWrapper;\nimport androidx.appcompat.widget.PopupMenu;\nimport androidx.core.content.ContextCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.CommentsAdapter.CommentCallback;\nimport awais.instagrabber.customviews.ProfilePicView;\nimport awais.instagrabber.databinding.ItemCommentBinding;\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.Utils;\n\npublic final class CommentViewHolder extends RecyclerView.ViewHolder {\n\n    private final ItemCommentBinding binding;\n    private final long currentUserId;\n    private final CommentCallback commentCallback;\n    @ColorInt\n    private int parentCommentHighlightColor;\n    private PopupMenu optionsPopup;\n\n    public CommentViewHolder(@NonNull final ItemCommentBinding binding,\n                             final long currentUserId,\n                             final CommentCallback commentCallback) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.currentUserId = currentUserId;\n        this.commentCallback = commentCallback;\n        final Context context = itemView.getContext();\n        if (context == null) return;\n        final Resources.Theme theme = context.getTheme();\n        if (theme == null) return;\n        final TypedValue typedValue = new TypedValue();\n        final boolean resolved = theme.resolveAttribute(R.attr.parentCommentHighlightColor, typedValue, true);\n        if (resolved) {\n            parentCommentHighlightColor = typedValue.data;\n        }\n    }\n\n    public void bind(final Comment comment, final boolean isReplyParent, final boolean isReply) {\n        if (comment == null) return;\n        itemView.setOnClickListener(v -> {\n            if (commentCallback != null) {\n                commentCallback.onClick(comment);\n            }\n        });\n        if (isReplyParent && parentCommentHighlightColor != 0) {\n            itemView.setBackgroundColor(parentCommentHighlightColor);\n        } else {\n            itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));\n        }\n        setupCommentText(comment, isReply);\n        binding.date.setText(comment.getDateTime());\n        setLikes(comment, isReply);\n        setReplies(comment, isReply);\n        setUser(comment, isReply);\n        setupOptions(comment, isReply);\n    }\n\n    private void setupCommentText(@NonNull final Comment comment, final boolean isReply) {\n        binding.comment.clearOnURLClickListeners();\n        binding.comment.clearOnHashtagClickListeners();\n        binding.comment.clearOnMentionClickListeners();\n        binding.comment.clearOnEmailClickListeners();\n        binding.comment.setText(comment.getText());\n        binding.comment.setTextSize(TypedValue.COMPLEX_UNIT_SP, isReply ? 12 : 14);\n        binding.comment.addOnHashtagListener(autoLinkItem -> {\n            final String originalText = autoLinkItem.getOriginalText();\n            if (commentCallback == null) return;\n            commentCallback.onHashtagClick(originalText);\n        });\n        binding.comment.addOnMentionClickListener(autoLinkItem -> {\n            final String originalText = autoLinkItem.getOriginalText();\n            if (commentCallback == null) return;\n            commentCallback.onMentionClick(originalText);\n\n        });\n        binding.comment.addOnEmailClickListener(autoLinkItem -> {\n            final String originalText = autoLinkItem.getOriginalText();\n            if (commentCallback == null) return;\n            commentCallback.onEmailClick(originalText);\n        });\n        binding.comment.addOnURLClickListener(autoLinkItem -> {\n            final String originalText = autoLinkItem.getOriginalText();\n            if (commentCallback == null) return;\n            commentCallback.onURLClick(originalText);\n        });\n        binding.comment.setOnLongClickListener(v -> {\n            Utils.copyText(itemView.getContext(), comment.getText());\n            return true;\n        });\n        binding.comment.setOnClickListener(v -> commentCallback.onClick(comment));\n    }\n\n    private void setUser(@NonNull final Comment comment, final boolean isReply) {\n        final User user = comment.getUser();\n        if (user == null) return;\n        binding.username.setUsername(user.getUsername(), user.isVerified());\n        binding.username.setTextAppearance(itemView.getContext(), isReply ? R.style.TextAppearance_MaterialComponents_Subtitle2\n                                                                          : R.style.TextAppearance_MaterialComponents_Subtitle1);\n        binding.username.setOnClickListener(v -> {\n            if (commentCallback == null) return;\n            commentCallback.onMentionClick(\"@\" + user.getUsername());\n        });\n        binding.profilePic.setImageURI(user.getProfilePicUrl());\n        binding.profilePic.setSize(isReply ? ProfilePicView.Size.SMALLER : ProfilePicView.Size.SMALL);\n        binding.profilePic.setOnClickListener(v -> {\n            if (commentCallback == null) return;\n            commentCallback.onMentionClick(\"@\" + user.getUsername());\n        });\n    }\n\n    private void setLikes(@NonNull final Comment comment, final boolean isReply) {\n        binding.likes.setText(String.valueOf(comment.getCommentLikeCount()));\n        binding.likes.setOnLongClickListener(v -> {\n            if (commentCallback == null) return false;\n            commentCallback.onViewLikes(comment);\n            return true;\n        });\n        if (currentUserId == 0) { // not logged in\n            binding.likes.setOnClickListener(v -> {\n                if (commentCallback == null) return;\n                commentCallback.onViewLikes(comment);\n            });\n            return;\n        }\n        final boolean liked = comment.getLiked();\n        final int resId = liked ? R.drawable.ic_like : R.drawable.ic_not_liked;\n        binding.likes.setCompoundDrawablesRelativeWithSize(ContextCompat.getDrawable(itemView.getContext(), resId), null, null, null);\n        binding.likes.setOnClickListener(v -> {\n            if (commentCallback == null) return;\n            // toggle like\n            commentCallback.onLikeClick(comment, !liked, isReply);\n        });\n    }\n\n    private void setReplies(@NonNull final Comment comment, final boolean isReply) {\n        final int replies = comment.getChildCommentCount();\n        binding.replies.setVisibility(View.VISIBLE);\n        final String text = isReply ? \"\" : String.valueOf(replies);\n        binding.replies.setText(text);\n        binding.replies.setOnClickListener(v -> {\n            if (commentCallback == null) return;\n            commentCallback.onRepliesClick(comment);\n        });\n    }\n\n    private void setupOptions(final Comment comment, final boolean isReply) {\n        binding.options.setOnClickListener(v -> {\n            if (optionsPopup == null) {\n                createOptionsPopupMenu(comment, isReply);\n            }\n            if (optionsPopup == null) return;\n            optionsPopup.show();\n        });\n    }\n\n    private void createOptionsPopupMenu(final Comment comment, final boolean isReply) {\n        if (optionsPopup == null) {\n            final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(itemView.getContext(), R.style.popupMenuStyle);\n            optionsPopup = new PopupMenu(themeWrapper, binding.options);\n        } else {\n            optionsPopup.getMenu().clear();\n        }\n        optionsPopup.getMenuInflater().inflate(R.menu.comment_options_menu, optionsPopup.getMenu());\n        final User user = comment.getUser();\n        if (currentUserId == 0 || user == null || user.getPk() != currentUserId) {\n            final Menu menu = optionsPopup.getMenu();\n            menu.removeItem(R.id.delete);\n        }\n        optionsPopup.setOnMenuItemClickListener(item -> {\n            if (commentCallback == null) return false;\n            int itemId = item.getItemId();\n            if (itemId == R.id.translate) {\n                commentCallback.onTranslate(comment);\n                return true;\n            }\n            if (itemId == R.id.delete) {\n                commentCallback.onDelete(comment, isReply);\n            }\n            return true;\n        });\n    }\n\n    // private void setupReply(final Comment comment) {\n    //     if (!isLoggedIn) {\n    //         binding.reply.setVisibility(View.GONE);\n    //         return;\n    //     }\n    //     binding.reply.setOnClickListener(v -> {\n    //         if (commentCallback == null) return;\n    //         // toggle like\n    //         commentCallback.onReplyClick(comment);\n    //     });\n    // }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/DiscoverViewHolder.java",
    "content": "//package awais.instagrabber.adapters.viewholder;\n//\n//import android.view.View;\n//import android.widget.ImageView;\n//\n//import androidx.annotation.NonNull;\n//import androidx.recyclerview.widget.RecyclerView;\n//\n//import com.facebook.drawee.view.SimpleDraweeView;\n//\n//import awais.instagrabber.R;\n//\n//public final class DiscoverViewHolder extends RecyclerView.ViewHolder {\n//    public final SimpleDraweeView postImage;\n//    public final ImageView typeIcon;\n//    public final View selectedView;\n//    // public final View progressView;\n//\n//    public DiscoverViewHolder(@NonNull final View itemView) {\n//        super(itemView);\n//        typeIcon = itemView.findViewById(R.id.typeIcon);\n//        postImage = itemView.findViewById(R.id.postImage);\n//        selectedView = itemView.findViewById(R.id.selectedView);\n//        // progressView = itemView.findViewById(R.id.progressView);\n//    }\n//}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/FavoriteViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.adapters.FavoritesAdapter;\nimport awais.instagrabber.databinding.ItemSearchResultBinding;\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.utils.Constants;\n\npublic class FavoriteViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = \"FavoriteViewHolder\";\n\n    private final ItemSearchResultBinding binding;\n\n    public FavoriteViewHolder(@NonNull final ItemSearchResultBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n        binding.verified.setVisibility(View.GONE);\n    }\n\n    public void bind(final Favorite model,\n                     final FavoritesAdapter.OnFavoriteClickListener clickListener,\n                     final FavoritesAdapter.OnFavoriteLongClickListener longClickListener) {\n        // Log.d(TAG, \"bind: \" + model);\n        if (model == null) return;\n        itemView.setOnClickListener(v -> {\n            if (clickListener == null) return;\n            clickListener.onClick(model);\n        });\n        itemView.setOnLongClickListener(v -> {\n            if (clickListener == null) return false;\n            return longClickListener.onLongClick(model);\n        });\n        if (model.getType() == FavoriteType.HASHTAG) {\n            binding.profilePic.setImageURI(Constants.DEFAULT_HASH_TAG_PIC);\n        } else {\n            binding.profilePic.setImageURI(model.getPicUrl());\n        }\n        binding.title.setVisibility(View.VISIBLE);\n        binding.subtitle.setText(model.getDisplayName());\n        String query = model.getQuery();\n        switch (model.getType()) {\n            case HASHTAG:\n                query = \"#\" + query;\n                break;\n            case USER:\n                query = \"@\" + query;\n                break;\n            case LOCATION:\n                binding.title.setVisibility(View.GONE);\n                break;\n            default:\n                // do nothing\n        }\n        binding.title.setText(query);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.net.Uri;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.DimenRes;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;\nimport com.facebook.imagepipeline.common.ResizeOptions;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.databinding.ItemFeedGridBinding;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\n\nimport static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;\n\npublic class FeedGridItemViewHolder extends RecyclerView.ViewHolder {\n    private final ItemFeedGridBinding binding;\n\n    public FeedGridItemViewHolder(@NonNull final ItemFeedGridBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final int position,\n                     @NonNull final Media media,\n                     @NonNull final PostsLayoutPreferences layoutPreferences,\n                     final FeedAdapterV2.FeedItemCallback feedItemCallback,\n                     final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,\n                     final boolean selectionModeActive,\n                     final boolean selected) {\n        itemView.setOnClickListener(v -> {\n            if (!selectionModeActive && feedItemCallback != null) {\n                feedItemCallback.onPostClick(media);\n                return;\n            }\n            if (selectionModeActive && adapterSelectionCallback != null) {\n                adapterSelectionCallback.onPostClick(position, media);\n            }\n        });\n        if (adapterSelectionCallback != null) {\n            itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, media));\n        }\n        binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE);\n        // for rounded borders (clip view to background shape)\n        itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());\n        if (layoutPreferences.getType() == STAGGERED_GRID) {\n            final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();\n            binding.postImage.setAspectRatio(aspectRatio);\n        } else {\n            binding.postImage.setAspectRatio(1);\n        }\n        setUserDetails(media, layoutPreferences);\n        String thumbnailUrl = null;\n        final int typeIconRes;\n        final MediaItemType mediaType = media.getType();\n        if (mediaType == null) return;\n        switch (mediaType) {\n            case MEDIA_TYPE_IMAGE:\n                typeIconRes = -1;\n                thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);\n                break;\n            case MEDIA_TYPE_VIDEO:\n                thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);\n                typeIconRes = R.drawable.exo_icon_play;\n                break;\n            case MEDIA_TYPE_SLIDER:\n                final List<Media> sliderItems = media.getCarouselMedia();\n                if (sliderItems != null) {\n                    final Media child = sliderItems.get(0);\n                    if (child != null) {\n                        thumbnailUrl = ResponseBodyUtils.getThumbUrl(child);\n                        if (layoutPreferences.getType() == STAGGERED_GRID) {\n                            final float childAspectRatio = (float) child.getOriginalWidth() / child.getOriginalHeight();\n                            binding.postImage.setAspectRatio(childAspectRatio);\n                        }\n                    }\n                }\n                typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;\n                break;\n            default:\n                typeIconRes = -1;\n                thumbnailUrl = null;\n        }\n        setThumbImage(thumbnailUrl);\n        if (typeIconRes <= 0) {\n            binding.typeIcon.setVisibility(View.GONE);\n        } else {\n            binding.typeIcon.setVisibility(View.VISIBLE);\n            binding.typeIcon.setImageResource(typeIconRes);\n        }\n        binding.downloaded.setVisibility(View.GONE);\n        final Context context = itemView.getContext();\n        if (context == null) {\n            return;\n        }\n        AppExecutors.INSTANCE.getTasksThread().execute(() -> {\n            final List<Boolean> checkList = DownloadUtils.checkDownloaded(media, context);\n            if (checkList.isEmpty()) {\n                return;\n            }\n            AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                switch (media.getType()) {\n                    case MEDIA_TYPE_IMAGE:\n                    case MEDIA_TYPE_VIDEO:\n                        binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);\n                        binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(R.color.green_A400)));\n                        break;\n                    case MEDIA_TYPE_SLIDER:\n                        binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);\n                        final List<Media> carouselMedia = media.getCarouselMedia();\n                        boolean allDownloaded = checkList.size() == (carouselMedia == null ? 0 : carouselMedia.size());\n                        if (allDownloaded) {\n                            allDownloaded = checkList.stream().allMatch(downloaded -> downloaded);\n                        }\n                        binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(\n                                allDownloaded ? R.color.green_A400 : R.color.yellow_400)));\n                        break;\n                    default:\n                }\n            });\n        });\n    }\n\n    private void setThumbImage(final String thumbnailUrl) {\n        if (TextUtils.isEmpty(thumbnailUrl)) {\n            binding.postImage.setController(null);\n            return;\n        }\n        final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))\n                                                               .setResizeOptions(ResizeOptions.forDimensions(binding.postImage.getWidth(),\n                                                                                                             binding.postImage.getHeight()))\n                                                               .setLocalThumbnailPreviewsEnabled(true)\n                                                               .setProgressiveRenderingEnabled(true)\n                                                               .build();\n        final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()\n                                                              .setImageRequest(requestBuilder)\n                                                              .setOldController(binding.postImage.getController());\n        binding.postImage.setController(builder.build());\n    }\n\n    private void setUserDetails(@NonNull final Media media,\n                                @NonNull final PostsLayoutPreferences layoutPreferences) {\n        final User user = media.getUser();\n        if (layoutPreferences.isAvatarVisible()) {\n            if (user == null) {\n                binding.profilePic.setVisibility(View.GONE);\n            } else {\n                final String profilePicUrl = user.getProfilePicUrl();\n                if (TextUtils.isEmpty(profilePicUrl)) {\n                    binding.profilePic.setVisibility(View.GONE);\n                } else {\n                    binding.profilePic.setVisibility(View.VISIBLE);\n                    binding.profilePic.setImageURI(profilePicUrl);\n                }\n            }\n            final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();\n            @DimenRes final int dimenRes;\n            switch (layoutPreferences.getProfilePicSize()) {\n                case SMALL:\n                    dimenRes = R.dimen.profile_pic_size_small;\n                    break;\n                case TINY:\n                    dimenRes = R.dimen.profile_pic_size_tiny;\n                    break;\n                default:\n                case REGULAR:\n                    dimenRes = R.dimen.profile_pic_size_regular;\n                    break;\n            }\n            final int dimensionPixelSize = itemView.getResources().getDimensionPixelSize(dimenRes);\n            layoutParams.width = dimensionPixelSize;\n            layoutParams.height = dimensionPixelSize;\n            binding.profilePic.requestLayout();\n        } else {\n            binding.profilePic.setVisibility(View.GONE);\n        }\n        if (layoutPreferences.isNameVisible()) {\n            if (user == null) {\n                binding.name.setVisibility(View.GONE);\n            } else {\n                final String username = user.getUsername();\n                if (username == null) {\n                    binding.name.setVisibility(View.GONE);\n                } else {\n                    binding.name.setVisibility(View.VISIBLE);\n                    binding.name.setText(username);\n                }\n            }\n        } else {\n            binding.name.setVisibility(View.GONE);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.adapters.FeedStoriesAdapter;\nimport awais.instagrabber.databinding.ItemHighlightBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic final class FeedStoryViewHolder extends RecyclerView.ViewHolder {\n\n    private final ItemHighlightBinding binding;\n\n    public FeedStoryViewHolder(final ItemHighlightBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final Story model,\n                     final int position,\n                     final FeedStoriesAdapter.OnFeedStoryClickListener listener) {\n        if (model == null) return;\n        binding.getRoot().setOnClickListener(v -> {\n            if (listener == null) return;\n            listener.onFeedStoryClick(model, position);\n        });\n        binding.getRoot().setOnLongClickListener(v -> {\n            if (listener != null) listener.onFeedStoryLongClick(model, position);\n            return true;\n        });\n        final User profileModel = model.getUser();\n        binding.title.setText(profileModel.getUsername());\n        final boolean isFullyRead =\n                model.getSeen() != null &&\n                model.getSeen().equals(model.getLatestReelMedia());\n        binding.title.setAlpha(isFullyRead ? 0.5F : 1.0F);\n        binding.icon.setImageURI(profileModel.getProfilePicUrl());\n        binding.icon.setAlpha(isFullyRead ? 0.5F : 1.0F);\n\n        if (model.getBroadcast() != null) binding.icon.setStoriesBorder(2);\n        else if (model.getHasBestiesMedia() == true) binding.icon.setStoriesBorder(1);\n        else binding.icon.setStoriesBorder(0);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/FilterViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.graphics.Bitmap;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Collection;\n\nimport awais.instagrabber.adapters.FiltersAdapter;\nimport awais.instagrabber.databinding.ItemFilterBinding;\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.BitmapUtils;\nimport jp.co.cyberagent.android.gpuimage.GPUImage;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\n\npublic class FilterViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = FilterViewHolder.class.getSimpleName();\n\n    private final ItemFilterBinding binding;\n    private final Collection<GPUImageFilter> tuneFilters;\n    private final FiltersAdapter.OnFilterClickListener onFilterClickListener;\n    private final AppExecutors appExecutors;\n\n    public FilterViewHolder(@NonNull final ItemFilterBinding binding,\n                            final Collection<GPUImageFilter> tuneFilters,\n                            final FiltersAdapter.OnFilterClickListener onFilterClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.tuneFilters = tuneFilters;\n        this.onFilterClickListener = onFilterClickListener;\n        appExecutors = AppExecutors.INSTANCE;\n    }\n\n    public void bind(final int position, final String originalKey, final Bitmap originalBitmap, final Filter<?> item, final boolean isSelected) {\n        if (originalBitmap == null || item == null) return;\n        if (onFilterClickListener != null) {\n            itemView.setOnClickListener(v -> onFilterClickListener.onClick(position, item));\n        }\n        if (item.getLabel() != -1) {\n            binding.name.setVisibility(View.VISIBLE);\n            binding.name.setText(item.getLabel());\n            binding.name.setSelected(isSelected);\n        } else {\n            binding.name.setVisibility(View.GONE);\n        }\n        final String filterKey = item.getLabel() + \"_\" + originalKey;\n        // avoid resetting the bitmap\n        if (binding.preview.getTag() != null && binding.preview.getTag().equals(filterKey)) return;\n        binding.preview.setTag(filterKey);\n        final Bitmap bitmap = BitmapUtils.getBitmapFromMemCache(filterKey);\n        if (bitmap == null) {\n            final GPUImageFilter filter = item.getInstance();\n            appExecutors.getTasksThread().submit(() -> {\n                GPUImage.getBitmapForMultipleFilters(\n                        originalBitmap,\n                        ImmutableList.<GPUImageFilter>builder().add(filter).addAll(tuneFilters).build(),\n                        filteredBitmap -> {\n                            BitmapUtils.addBitmapToMemoryCache(filterKey, filteredBitmap, true);\n                            appExecutors.getMainThread().execute(() -> binding.getRoot().post(() -> binding.preview.setImageBitmap(filteredBitmap)));\n                        }\n                );\n            });\n            return;\n        }\n        binding.getRoot().post(() -> binding.preview.setImageBitmap(bitmap));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.databinding.ItemFollowBinding;\nimport awais.instagrabber.repositories.responses.User;\n\npublic final class FollowsViewHolder extends RecyclerView.ViewHolder {\n\n    private final ItemFollowBinding binding;\n\n    public FollowsViewHolder(@NonNull final ItemFollowBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final User model,\n                     final View.OnClickListener onClickListener) {\n        if (model == null) return;\n        itemView.setTag(model);\n        itemView.setOnClickListener(onClickListener);\n        binding.username.setUsername(\"@\" + model.getUsername(), model.isVerified());\n        binding.fullName.setText(model.getFullName());\n        binding.profilePic.setImageURI(model.getProfilePicUrl());\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/HighlightViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.databinding.ItemHighlightBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic final class HighlightViewHolder extends RecyclerView.ViewHolder {\n\n    private final ItemHighlightBinding binding;\n\n    public HighlightViewHolder(final ItemHighlightBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final Story model) {\n        if (model == null) return;\n        binding.title.setText(model.getTitle());\n        binding.icon.setImageURI(model.getCoverMedia().getCroppedImageVersion().getUrl());\n        // binding.getRoot().setOnClickListener(v -> {\n        //     if (listener == null) return;\n        //     listener.onFeedStoryClick(model, position);\n        // });\n        // final ProfileModel profileModel = model.getProfileModel();\n        // binding.title.setText(profileModel.getUsername());\n        // binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);\n        // binding.icon.setImageURI(profileModel.getSdProfilePic());\n        // binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.text.TextUtils;\nimport android.view.View;\n\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;\nimport awais.instagrabber.databinding.ItemNotificationBinding;\nimport awais.instagrabber.models.enums.NotificationType;\nimport awais.instagrabber.repositories.responses.notification.Notification;\nimport awais.instagrabber.repositories.responses.notification.NotificationArgs;\n\npublic final class NotificationViewHolder extends RecyclerView.ViewHolder {\n    private final ItemNotificationBinding binding;\n\n    public NotificationViewHolder(final ItemNotificationBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final Notification model,\n                     final OnNotificationClickListener notificationClickListener) {\n        if (model == null) return;\n        int text = -1;\n        CharSequence subtext = null;\n        final NotificationArgs args = model.getArgs();\n        switch (model.getType()) {\n            case LIKE:\n                text = R.string.liked_notif;\n                break;\n            case COMMENT: // untested\n                text = R.string.comment_notif;\n                subtext = args.getText();\n                break;\n            case TAGGED:\n                text = R.string.tagged_notif;\n                break;\n            case FOLLOW:\n                text = R.string.follow_notif;\n                break;\n            case REQUEST:\n                text = R.string.request_notif;\n                break;\n            case COMMENT_MENTION:\n            case COMMENT_LIKE:\n            case TAGGED_COMMENT:\n            case RESPONDED_STORY:\n                subtext = args.getText();\n                break;\n            case AYML:\n                subtext = args.getFullName();\n                break;\n        }\n        binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? args.getText() : subtext);\n        if (text == -1 && subtext != null) {\n            binding.tvComment.setText(args.getText());\n            binding.tvComment.setVisibility(TextUtils.isEmpty(args.getText()) || args.getText().equals(args.getFullName())\n                    ? View.GONE : View.VISIBLE);\n            binding.tvSubComment.setText(subtext);\n            binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);\n        } else if (text != -1) {\n            binding.tvComment.setText(text);\n            binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);\n        }\n\n        binding.tvDate.setVisibility(model.getType() == NotificationType.AYML ? View.GONE : View.VISIBLE);\n        if (model.getType() != NotificationType.AYML) {\n            binding.tvDate.setText(args.getDateTime());\n        }\n\n        binding.isVerified.setVisibility(args.isVerified() ? View.VISIBLE : View.GONE);\n\n        binding.tvUsername.setText(args.getUsername());\n        binding.ivProfilePic.setImageURI(args.getProfilePic());\n        binding.ivProfilePic.setOnClickListener(v -> {\n            if (notificationClickListener == null) return;\n            notificationClickListener.onProfileClick(args.getUsername());\n        });\n\n        if (model.getType() == NotificationType.AYML) {\n            binding.ivPreviewPic.setVisibility(View.GONE);\n        } else if (args.getMedia() == null) {\n            binding.ivPreviewPic.setVisibility(View.INVISIBLE);\n        } else {\n            binding.ivPreviewPic.setVisibility(View.VISIBLE);\n            binding.ivPreviewPic.setImageURI(args.getMedia().get(0).getImage());\n            binding.ivPreviewPic.setOnClickListener(v -> {\n                if (notificationClickListener == null) return;\n                notificationClickListener.onPreviewClick(model);\n            });\n        }\n\n        itemView.setOnClickListener(v -> {\n            if (notificationClickListener == null) return;\n            notificationClickListener.onNotificationClick(model);\n        });\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/SearchItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.ItemSearchResultBinding;\nimport awais.instagrabber.fragments.search.SearchCategoryFragment.OnSearchItemClickListener;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.Place;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.search.SearchItem;\n\npublic class SearchItemViewHolder extends RecyclerView.ViewHolder {\n\n    private final ItemSearchResultBinding binding;\n    private final OnSearchItemClickListener onSearchItemClickListener;\n\n    public SearchItemViewHolder(@NonNull final ItemSearchResultBinding binding,\n                                final OnSearchItemClickListener onSearchItemClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.onSearchItemClickListener = onSearchItemClickListener;\n    }\n\n    public void bind(final SearchItem searchItem) {\n        if (searchItem == null) return;\n        final FavoriteType type = searchItem.getType();\n        if (type == null) return;\n        String title;\n        String subtitle;\n        String picUrl;\n        boolean isVerified = false;\n        switch (type) {\n            case USER:\n                final User user = searchItem.getUser();\n                title = \"@\" + user.getUsername();\n                subtitle = user.getFullName();\n                picUrl = user.getProfilePicUrl();\n                isVerified = user.isVerified();\n                break;\n            case HASHTAG:\n                final Hashtag hashtag = searchItem.getHashtag();\n                title = \"#\" + hashtag.getName();\n                subtitle = hashtag.getSearchResultSubtitle();\n                picUrl = \"res:/\" + R.drawable.ic_hashtag;\n                break;\n            case LOCATION:\n                final Place place = searchItem.getPlace();\n                title = place.getTitle();\n                subtitle = place.getSubtitle();\n                picUrl = \"res:/\" + R.drawable.ic_location;\n                break;\n            default:\n                return;\n        }\n        itemView.setOnClickListener(v -> {\n            if (onSearchItemClickListener != null) {\n                onSearchItemClickListener.onSearchItemClick(searchItem);\n            }\n        });\n        binding.delete.setVisibility(searchItem.isRecent() ? View.VISIBLE : View.GONE);\n        if (searchItem.isRecent()) {\n            binding.delete.setEnabled(true);\n            binding.delete.setOnClickListener(v -> {\n                if (onSearchItemClickListener != null) {\n                    binding.delete.setEnabled(false);\n                    onSearchItemClickListener.onSearchItemDelete(searchItem, type);\n                }\n            });\n        }\n        binding.title.setText(title);\n        binding.subtitle.setText(subtitle);\n        binding.profilePic.setImageURI(picUrl);\n        binding.verified.setVisibility(isVerified ? View.VISIBLE : View.GONE);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/SliderItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.adapters.SliderItemsAdapter;\nimport awais.instagrabber.repositories.responses.Media;\n\npublic abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = \"FeedSliderItemViewHolder\";\n\n    public SliderItemViewHolder(@NonNull final View itemView) {\n        super(itemView);\n    }\n\n    public abstract void bind(final Media media,\n                              final int position,\n                              final SliderItemsAdapter.SliderCallback sliderCallback);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.graphics.drawable.Animatable;\nimport android.net.Uri;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.imagepipeline.image.ImageInfo;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\n\nimport awais.instagrabber.adapters.SliderItemsAdapter;\nimport awais.instagrabber.customviews.drawee.AnimatedZoomableController;\nimport awais.instagrabber.customviews.drawee.DoubleTapGestureListener;\nimport awais.instagrabber.databinding.ItemSliderPhotoBinding;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic class SliderPhotoViewHolder extends SliderItemViewHolder {\n    private static final String TAG = \"FeedSliderPhotoViewHolder\";\n\n    private final ItemSliderPhotoBinding binding;\n\n    public SliderPhotoViewHolder(@NonNull final ItemSliderPhotoBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(@NonNull final Media model,\n                     final int position,\n                     final SliderItemsAdapter.SliderCallback sliderCallback) {\n        final ImageRequest requestBuilder = ImageRequestBuilder\n                .newBuilderWithSource(Uri.parse(ResponseBodyUtils.getImageUrl(model)))\n                .setLocalThumbnailPreviewsEnabled(true)\n                .build();\n        binding.getRoot()\n               .setController(Fresco.newDraweeControllerBuilder()\n                                    .setImageRequest(requestBuilder)\n                                    .setControllerListener(new BaseControllerListener<ImageInfo>() {\n                                        @Override\n                                        public void onFailure(final String id, final Throwable throwable) {\n                                            if (sliderCallback != null) {\n                                                sliderCallback.onThumbnailLoaded(position);\n                                            }\n                                        }\n\n                                        @Override\n                                        public void onFinalImageSet(final String id,\n                                                                    final ImageInfo imageInfo,\n                                                                    final Animatable animatable) {\n                                            if (sliderCallback != null) {\n                                                sliderCallback.onThumbnailLoaded(position);\n                                            }\n                                        }\n                                    })\n                                    .setLowResImageRequest(ImageRequest.fromUri(ResponseBodyUtils.getThumbUrl(model)))\n                                    .build());\n        final DoubleTapGestureListener tapListener = new DoubleTapGestureListener(binding.getRoot()) {\n            @Override\n            public boolean onSingleTapConfirmed(final MotionEvent e) {\n                if (sliderCallback != null) {\n                    sliderCallback.onItemClicked(position, model, binding.getRoot());\n                }\n                return super.onSingleTapConfirmed(e);\n            }\n        };\n        binding.getRoot().setTapListener(tapListener);\n        final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance();\n        zoomableController.setMaxScaleFactor(3f);\n        binding.getRoot().setZoomableController(zoomableController);\n        binding.getRoot().setZoomingEnabled(true);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.annotation.SuppressLint;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\n\nimport java.util.List;\n\nimport awais.instagrabber.adapters.SliderItemsAdapter;\nimport awais.instagrabber.customviews.VideoPlayerCallbackAdapter;\nimport awais.instagrabber.customviews.VideoPlayerViewHelper;\nimport awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.MediaCandidate;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class SliderVideoViewHolder extends SliderItemViewHolder {\n    private static final String TAG = \"SliderVideoViewHolder\";\n\n    private final LayoutVideoPlayerWithThumbnailBinding binding;\n    private final boolean loadVideoOnItemClick;\n\n    private VideoPlayerViewHelper videoPlayerViewHelper;\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    public SliderVideoViewHolder(@NonNull final LayoutVideoPlayerWithThumbnailBinding binding,\n                                 final boolean loadVideoOnItemClick) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.loadVideoOnItemClick = loadVideoOnItemClick;\n        final GestureDetector.OnGestureListener videoPlayerViewGestureListener = new GestureDetector.SimpleOnGestureListener() {\n            @Override\n            public boolean onSingleTapConfirmed(final MotionEvent e) {\n                binding.playerView.performClick();\n                return true;\n            }\n        };\n        final GestureDetector gestureDetector = new GestureDetector(itemView.getContext(), videoPlayerViewGestureListener);\n        binding.playerView.setOnTouchListener((v, event) -> {\n            gestureDetector.onTouchEvent(event);\n            return true;\n        });\n    }\n\n    public void bind(@NonNull final Media media,\n                     final int position,\n                     final SliderItemsAdapter.SliderCallback sliderCallback) {\n        final float vol = settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;\n        final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {\n\n            @Override\n            public void onThumbnailClick() {\n                if (sliderCallback != null) {\n                    sliderCallback.onItemClicked(position, media, binding.getRoot());\n                }\n            }\n\n            @Override\n            public void onThumbnailLoaded() {\n                if (sliderCallback != null) {\n                    sliderCallback.onThumbnailLoaded(position);\n                }\n            }\n\n            @Override\n            public void onPlayerViewLoaded() {\n                // binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);\n                final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();\n                final int requiredWidth = Utils.displayMetrics.widthPixels;\n                final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());\n                layoutParams.width = requiredWidth;\n                layoutParams.height = resultingHeight;\n                binding.playerView.requestLayout();\n                // setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);\n            }\n\n            @Override\n            public void onPlay() {\n                if (sliderCallback != null) {\n                    sliderCallback.onPlayerPlay(position);\n                }\n            }\n\n            @Override\n            public void onPause() {\n                if (sliderCallback != null) {\n                    sliderCallback.onPlayerPause(position);\n                }\n            }\n\n            @Override\n            public void onRelease() {\n                if (sliderCallback != null) {\n                    sliderCallback.onPlayerRelease(position);\n                }\n            }\n\n            @Override\n            public void onFullScreenModeChanged(final boolean isFullScreen, final StyledPlayerView playerView) {\n                if (sliderCallback != null) {\n                    sliderCallback.onFullScreenModeChanged(isFullScreen, playerView);\n                }\n            }\n\n            @Override\n            public boolean isInFullScreen() {\n                if (sliderCallback != null) {\n                    return sliderCallback.isInFullScreen();\n                }\n                return false;\n            }\n        };\n        final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();\n        String videoUrl = null;\n        final List<MediaCandidate> videoVersions = media.getVideoVersions();\n        if (videoVersions != null && !videoVersions.isEmpty()) {\n            final MediaCandidate videoVersion = videoVersions.get(0);\n            if (videoVersion != null) {\n                videoUrl = videoVersion.getUrl();\n            }\n        }\n        if (videoUrl == null) return;\n        videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),\n                                                          binding,\n                                                          videoUrl,\n                                                          vol,\n                                                          aspectRatio,\n                                                          ResponseBodyUtils.getThumbUrl(media),\n                                                          loadVideoOnItemClick,\n                                                          videoPlayerCallback);\n        binding.playerView.setOnClickListener(v -> {\n            if (sliderCallback != null) {\n                sliderCallback.onItemClicked(position, media, binding.getRoot());\n            }\n        });\n    }\n\n    public void pause() {\n        if (videoPlayerViewHelper == null) return;\n        videoPlayerViewHelper.pause();\n    }\n\n    public void releasePlayer() {\n        if (videoPlayerViewHelper == null) return;\n        videoPlayerViewHelper.releasePlayer();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.view.View;\n\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;\nimport awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;\nimport awais.instagrabber.databinding.ItemNotificationBinding;\nimport awais.instagrabber.repositories.responses.stories.Story;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic final class StoryListViewHolder extends RecyclerView.ViewHolder {\n    private final ItemNotificationBinding binding;\n\n    public StoryListViewHolder(final ItemNotificationBinding binding) {\n        super(binding.getRoot());\n        this.binding = binding;\n    }\n\n    public void bind(final Story model,\n                     final OnFeedStoryClickListener notificationClickListener) {\n        if (model == null) return;\n\n        final int storiesCount = model.getMediaCount();\n        binding.tvComment.setVisibility(View.VISIBLE);\n        binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount));\n\n        binding.tvSubComment.setVisibility(View.GONE);\n\n        binding.tvDate.setText(model.getDateTime());\n\n        binding.tvUsername.setText(model.getUser().getUsername());\n        binding.ivProfilePic.setImageURI(model.getUser().getProfilePicUrl());\n        binding.ivProfilePic.setOnClickListener(v -> {\n            if (notificationClickListener == null) return;\n            notificationClickListener.onProfileClick(model.getUser().getUsername());\n        });\n\n        if (model.getItems() != null && model.getItems().size() > 0) {\n            binding.ivPreviewPic.setVisibility(View.VISIBLE);\n            binding.ivPreviewPic.setImageURI(ResponseBodyUtils.getThumbUrl(model.getItems().get(0)));\n        } else binding.ivPreviewPic.setVisibility(View.INVISIBLE);\n\n        float alpha = model.getSeen() != null && model.getSeen().equals(model.getLatestReelMedia())\n                ? 0.5F : 1.0F;\n        binding.ivProfilePic.setAlpha(alpha);\n        binding.ivPreviewPic.setAlpha(alpha);\n        binding.tvUsername.setAlpha(alpha);\n        binding.tvComment.setAlpha(alpha);\n        binding.tvDate.setAlpha(alpha);\n\n        itemView.setOnClickListener(v -> {\n            if (notificationClickListener == null) return;\n            notificationClickListener.onFeedStoryClick(model);\n        });\n    }\n\n    public void bind(final Story model,\n                     final int position,\n                     final OnHighlightStoryClickListener notificationClickListener) {\n        if (model == null) return;\n\n        final int storiesCount = model.getMediaCount();\n        binding.tvComment.setVisibility(View.VISIBLE);\n        binding.tvComment.setText(itemView.getResources().getQuantityString(R.plurals.stories_count, storiesCount, storiesCount));\n\n        binding.tvSubComment.setVisibility(View.GONE);\n\n        binding.tvUsername.setText(model.getDateTime());\n\n        binding.ivProfilePic.setVisibility(View.GONE);\n\n        binding.ivPreviewPic.setVisibility(View.VISIBLE);\n        binding.ivPreviewPic.setImageURI(model.getCoverImageVersion().getUrl());\n\n        itemView.setOnClickListener(v -> {\n            if (notificationClickListener == null) return;\n            notificationClickListener.onHighlightClick(model, position);\n        });\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/TabViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.annotation.SuppressLint;\nimport android.content.res.ColorStateList;\nimport android.graphics.drawable.Drawable;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.widget.ImageViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.color.MaterialColors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.TabsAdapter;\nimport awais.instagrabber.databinding.ItemTabOrderPrefBinding;\nimport awais.instagrabber.models.Tab;\n\npublic class TabViewHolder extends RecyclerView.ViewHolder {\n    private final ItemTabOrderPrefBinding binding;\n    private final TabsAdapter.TabAdapterCallback tabAdapterCallback;\n    private final int highlightColor;\n    private final Drawable originalBgColor;\n\n    private boolean draggable = true;\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    public TabViewHolder(@NonNull final ItemTabOrderPrefBinding binding,\n                         @NonNull final TabsAdapter.TabAdapterCallback tabAdapterCallback) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.tabAdapterCallback = tabAdapterCallback;\n        highlightColor = MaterialColors.getColor(itemView.getContext(), R.attr.colorControlHighlight, 0);\n        originalBgColor = itemView.getBackground();\n        binding.handle.setOnTouchListener((v, event) -> {\n            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {\n                tabAdapterCallback.onStartDrag(this);\n            }\n            return true;\n        });\n    }\n\n    public void bind(@NonNull final Tab tab,\n                     final boolean isInOthers,\n                     final boolean isCurrentFull) {\n        draggable = !isInOthers;\n        binding.icon.setImageResource(tab.getIconResId());\n        binding.title.setText(tab.getTitle());\n        binding.handle.setVisibility(isInOthers ? View.GONE : View.VISIBLE);\n        binding.addRemove.setImageResource(isInOthers ? R.drawable.ic_round_add_circle_24\n                                                      : R.drawable.ic_round_remove_circle_24);\n        final ColorStateList tintList = ColorStateList.valueOf(ContextCompat.getColor(\n                itemView.getContext(),\n                isInOthers ? R.color.green_500\n                           : R.color.red_500));\n        ImageViewCompat.setImageTintList(binding.addRemove, tintList);\n        binding.addRemove.setOnClickListener(v -> {\n            if (isInOthers) {\n                tabAdapterCallback.onAdd(tab);\n                return;\n            }\n            tabAdapterCallback.onRemove(tab);\n        });\n        final boolean enabled = tab.isRemovable()\n                && !(isInOthers && isCurrentFull); // All slots are full in current\n        binding.addRemove.setEnabled(enabled);\n        binding.addRemove.setAlpha(enabled ? 1 : 0.5F);\n    }\n\n    public boolean isDraggable() {\n        return draggable;\n    }\n\n    public void setDragging(final boolean isDragging) {\n        if (isDragging) {\n            if (highlightColor != 0) {\n                itemView.setBackgroundColor(highlightColor);\n            } else {\n                itemView.setAlpha(0.5F);\n            }\n            return;\n        }\n        itemView.setAlpha(1);\n        itemView.setBackground(originalBgColor);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder;\n\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.graphics.drawable.GradientDrawable;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.palette.graphics.Palette;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.facebook.common.executors.CallerThreadExecutor;\nimport com.facebook.common.references.CloseableReference;\nimport com.facebook.datasource.DataSource;\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.imagepipeline.core.ImagePipeline;\nimport com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;\nimport com.facebook.imagepipeline.image.CloseableImage;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DiscoverTopicsAdapter;\nimport awais.instagrabber.adapters.SavedCollectionsAdapter;\nimport awais.instagrabber.databinding.ItemDiscoverTopicBinding;\nimport awais.instagrabber.repositories.responses.discover.TopicCluster;\nimport awais.instagrabber.repositories.responses.saved.SavedCollection;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic class TopicClusterViewHolder extends RecyclerView.ViewHolder {\n    private final ItemDiscoverTopicBinding binding;\n    private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;\n    private final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener;\n\n    public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,\n                                  final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener,\n                                  final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.onTopicClickListener = onTopicClickListener;\n        this.onCollectionClickListener = onCollectionClickListener;\n    }\n\n    public void bind(final TopicCluster topicCluster) {\n        if (topicCluster == null) {\n            return;\n        }\n        final AtomicInteger titleColor = new AtomicInteger(-1);\n        final AtomicInteger backgroundColor = new AtomicInteger(-1);\n        if (onTopicClickListener != null) {\n            itemView.setOnClickListener(v -> onTopicClickListener.onTopicClick(\n                    topicCluster,\n                    binding.cover,\n                    titleColor.get(),\n                    backgroundColor.get()\n            ));\n            itemView.setOnLongClickListener(v -> {\n                onTopicClickListener.onTopicLongClick(topicCluster.getCoverMedia());\n                return true;\n            });\n        }\n        // binding.title.setTransitionName(\"title-\" + topicCluster.getId());\n        binding.cover.setTransitionName(\"cover-\" + topicCluster.getId());\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedia());\n        if (thumbUrl == null) {\n            binding.cover.setImageURI((String) null);\n        } else {\n            final ImageRequest imageRequest = ImageRequestBuilder\n                    .newBuilderWithSource(Uri.parse(thumbUrl))\n                    .build();\n            final ImagePipeline imagePipeline = Fresco.getImagePipeline();\n            final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline\n                    .fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());\n            dataSource.subscribe(new BaseBitmapDataSubscriber() {\n                @Override\n                public void onNewResultImpl(@Nullable Bitmap bitmap) {\n                    if (dataSource.isFinished()) {\n                        dataSource.close();\n                    }\n                    if (bitmap != null) {\n                        Palette.from(bitmap).generate(p -> {\n                            final Resources resources = itemView.getResources();\n                            int titleTextColor = resources.getColor(R.color.white);\n                            if (p != null) {\n                                final Palette.Swatch swatch = p.getDominantSwatch();\n                                if (swatch != null) {\n                                    backgroundColor.set(swatch.getRgb());\n                                    GradientDrawable gd = new GradientDrawable(\n                                            GradientDrawable.Orientation.TOP_BOTTOM,\n                                            new int[]{Color.TRANSPARENT, backgroundColor.get()});\n                                    titleTextColor = swatch.getTitleTextColor();\n                                    binding.background.setBackground(gd);\n                                }\n                            }\n                            titleColor.set(titleTextColor);\n                            binding.title.setTextColor(titleTextColor);\n                        });\n                    }\n                }\n\n                @Override\n                public void onFailureImpl(@NonNull DataSource dataSource) {\n                    dataSource.close();\n                }\n            }, CallerThreadExecutor.getInstance());\n            binding.cover.setImageRequest(imageRequest);\n        }\n        binding.title.setText(topicCluster.getTitle());\n    }\n\n    public void bind(final SavedCollection topicCluster) {\n        if (topicCluster == null) {\n            return;\n        }\n        final AtomicInteger titleColor = new AtomicInteger(-1);\n        final AtomicInteger backgroundColor = new AtomicInteger(-1);\n        if (onCollectionClickListener != null) {\n            itemView.setOnClickListener(v -> onCollectionClickListener.onCollectionClick(\n                    topicCluster,\n                    binding.getRoot(),\n                    binding.cover,\n                    binding.title,\n                    titleColor.get(),\n                    backgroundColor.get()\n            ));\n        }\n        // binding.title.setTransitionName(\"title-\" + topicCluster.getCollectionId());\n        binding.cover.setTransitionName(\"cover-\" + topicCluster.getCollectionId());\n        final Media coverMedia = topicCluster.getCoverMediaList() == null\n                ? topicCluster.getCoverMedia()\n                : topicCluster.getCoverMediaList().get(0);\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(coverMedia);\n        if (thumbUrl == null) {\n            binding.cover.setImageURI((String) null);\n        } else {\n            final ImageRequest imageRequest = ImageRequestBuilder\n                    .newBuilderWithSource(Uri.parse(thumbUrl))\n                    .build();\n            final ImagePipeline imagePipeline = Fresco.getImagePipeline();\n            final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline\n                    .fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());\n            dataSource.subscribe(new BaseBitmapDataSubscriber() {\n                @Override\n                public void onNewResultImpl(@Nullable Bitmap bitmap) {\n                    if (dataSource.isFinished()) {\n                        dataSource.close();\n                    }\n                    if (bitmap != null) {\n                        Palette.from(bitmap).generate(p -> {\n                            final Resources resources = itemView.getResources();\n                            int titleTextColor = resources.getColor(R.color.white);\n                            if (p != null) {\n                                final Palette.Swatch swatch = p.getDominantSwatch();\n                                if (swatch != null) {\n                                    backgroundColor.set(swatch.getRgb());\n                                    GradientDrawable gd = new GradientDrawable(\n                                            GradientDrawable.Orientation.TOP_BOTTOM,\n                                            new int[]{Color.TRANSPARENT, backgroundColor.get()}\n                                    );\n                                    titleTextColor = swatch.getTitleTextColor();\n                                    binding.background.setBackground(gd);\n                                }\n                            }\n                            titleColor.set(titleTextColor);\n                            binding.title.setTextColor(titleTextColor);\n                        });\n                    }\n                }\n\n                @Override\n                public void onFailureImpl(@NonNull DataSource dataSource) {\n                    dataSource.close();\n                }\n            }, CallerThreadExecutor.getInstance());\n            binding.cover.setImageRequest(imageRequest);\n        }\n        binding.title.setText(topicCluster.getCollectionName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/dialogs/KeywordsFilterDialogViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.dialogs;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.KeywordsFilterAdapter;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.utils.SettingsHelper;\n\npublic class KeywordsFilterDialogViewHolder extends RecyclerView.ViewHolder {\n\n    private final Button deleteButton;\n    private final TextView item;\n\n    public KeywordsFilterDialogViewHolder(@NonNull View itemView) {\n        super(itemView);\n        deleteButton = itemView.findViewById(R.id.keyword_delete);\n        item = itemView.findViewById(R.id.keyword_text);\n    }\n\n    public void bind(ArrayList<String> items, int position, Context context, KeywordsFilterAdapter adapter){\n        item.setText(items.get(position));\n        deleteButton.setOnClickListener(view -> {\n            final String s = items.get(position);\n            SettingsHelper settingsHelper = new SettingsHelper(context);\n            items.remove(position);\n            settingsHelper.putStringSet(PreferenceKeys.KEYWORD_FILTERS, new HashSet<>(items));\n            adapter.notifyDataSetChanged();\n            final String message = context.getString(R.string.removed_keywords, s);\n            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();\n        });\n    }\n\n    public Button getDeleteButton(){\n        return deleteButton;\n    }\n\n    public TextView getTextView(){\n        return item;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectInboxItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.content.res.Resources;\nimport android.graphics.Typeface;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.constraintlayout.widget.ConstraintSet;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.facebook.drawee.view.SimpleDraweeView;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;\nimport awais.instagrabber.databinding.LayoutDmInboxItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.DMUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {\n    // private static final String TAG = \"DMInboxItemVH\";\n    private final LayoutDmInboxItemBinding binding;\n    private final OnItemClickListener onClickListener;\n    private final List<SimpleDraweeView> multipleProfilePics;\n    private final int childSmallSize;\n    private final int childTinySize;\n\n    public DirectInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding,\n                                     final OnItemClickListener onClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.onClickListener = onClickListener;\n        multipleProfilePics = ImmutableList.of(\n                binding.multiPic1,\n                binding.multiPic2,\n                binding.multiPic3\n        );\n        childSmallSize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_small);\n        childTinySize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_tiny);\n    }\n\n    public void bind(final DirectThread thread) {\n        if (thread == null) return;\n        if (onClickListener != null) {\n            itemView.setOnClickListener((v) -> onClickListener.onItemClick(thread));\n        }\n        setProfilePics(thread);\n        setTitle(thread);\n        final List<DirectItem> items = thread.getItems();\n        if (items == null || items.isEmpty()) return;\n        final DirectItem item = thread.getFirstDirectItem();\n        if (item == null) return;\n        setDateTime(item);\n        setSubtitle(thread);\n        setReadState(thread);\n    }\n\n    private void setProfilePics(@NonNull final DirectThread thread) {\n        final List<User> users = thread.getUsers();\n        if (users.size() > 1) {\n            binding.profilePic.setVisibility(View.GONE);\n            binding.multiPicContainer.setVisibility(View.VISIBLE);\n            for (int i = 0; i < Math.min(3, users.size()); ++i) {\n                final User user = users.get(i);\n                final SimpleDraweeView view = multipleProfilePics.get(i);\n                view.setVisibility(user == null ? View.GONE : View.VISIBLE);\n                if (user == null) return;\n                final String profilePicUrl = user.getProfilePicUrl();\n                view.setImageURI(profilePicUrl);\n                setChildSize(view, users.size());\n                if (i == 1) {\n                    updateConstraints(view, users.size());\n                }\n                view.requestLayout();\n            }\n            return;\n        }\n        binding.profilePic.setVisibility(View.VISIBLE);\n        binding.multiPicContainer.setVisibility(View.GONE);\n        final String profilePicUrl = users.size() == 1 ? users.get(0).getProfilePicUrl() : null;\n        if (profilePicUrl == null) {\n            binding.profilePic.setController(null);\n            return;\n        }\n        binding.profilePic.setImageURI(profilePicUrl);\n    }\n\n    private void updateConstraints(final SimpleDraweeView view, final int length) {\n        final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) view.getLayoutParams();\n        if (length >= 2) {\n            layoutParams.endToEnd = ConstraintSet.PARENT_ID;\n            layoutParams.bottomToBottom = ConstraintSet.PARENT_ID;\n        }\n        if (length == 3) {\n            layoutParams.startToStart = ConstraintSet.PARENT_ID;\n            layoutParams.topToTop = ConstraintSet.PARENT_ID;\n        }\n    }\n\n    private void setChildSize(final SimpleDraweeView view, final int length) {\n        final int size = length == 3 ? childTinySize : childSmallSize;\n        final ConstraintLayout.LayoutParams viewLayoutParams = new ConstraintLayout.LayoutParams(size, size);\n        view.setLayoutParams(viewLayoutParams);\n    }\n\n    private void setTitle(@NonNull final DirectThread thread) {\n        final String threadTitle = thread.getThreadTitle();\n        binding.threadTitle.setText(threadTitle);\n    }\n\n    private void setSubtitle(@NonNull final DirectThread thread) {\n        final Resources resources = itemView.getResources();\n        final long viewerId = thread.getViewerId();\n//        final DirectThreadDirectStory directStory = thread.getDirectStory();\n//        if (directStory != null && !directStory.getItems().isEmpty()) {\n//            final DirectItem item = directStory.getItems().get(0);\n//            final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();\n//            final String username = DMUtils.getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);\n//            final String subtitle = DMUtils.getMediaSpecificSubtitle(username, resources, mediaType);\n//            binding.subtitle.setText(subtitle);\n//            return;\n//        }\n        final DirectItem item = thread.getFirstDirectItem();\n        if (item == null) return;\n        final String subtitle = DMUtils.getMessageString(thread, resources, viewerId, item);\n        binding.subtitle.setText(subtitle != null ? subtitle : \"\");\n    }\n\n    private void setDateTime(@NonNull final DirectItem item) {\n        final long timestamp = item.getTimestamp() / 1000;\n        final String dateTimeString = TextUtils.getRelativeDateTimeString(timestamp);\n        binding.tvDate.setText(dateTimeString);\n    }\n\n    private void setReadState(@NonNull final DirectThread thread) {\n        final boolean read = DMUtils.isRead(thread);\n        binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);\n        binding.threadTitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);\n        binding.subtitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemActionLogViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.graphics.Typeface;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.method.LinkMovementMethod;\nimport android.text.style.ClickableSpan;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.StyleSpan;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmActionLogBinding;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemActionLog;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.TextRange;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemActionLogViewHolder extends DirectItemViewHolder {\n    private static final String TAG = DirectItemActionLogViewHolder.class.getSimpleName();\n\n    private final LayoutDmActionLogBinding binding;\n\n    public DirectItemActionLogViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                         final LayoutDmActionLogBinding binding,\n                                         final User currentUser,\n                                         final DirectThread thread,\n                                         final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n        binding.tvMessage.setMovementMethod(LinkMovementMethod.getInstance());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final DirectItemActionLog actionLog = directItemModel.getActionLog();\n        final String text = actionLog.getDescription();\n        final SpannableStringBuilder sb = new SpannableStringBuilder(text);\n        final List<TextRange> bold = actionLog.getBold();\n        if (bold != null && !bold.isEmpty()) {\n            for (final TextRange textRange : bold) {\n                final StyleSpan boldStyleSpan = new StyleSpan(Typeface.BOLD);\n                sb.setSpan(boldStyleSpan, textRange.getStart(), textRange.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n            }\n        }\n        final List<TextRange> textAttributes = actionLog.getTextAttributes();\n        if (textAttributes != null && !textAttributes.isEmpty()) {\n            for (final TextRange textAttribute : textAttributes) {\n                if (!TextUtils.isEmpty(textAttribute.getColor())) {\n                    final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));\n                    sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n                if (!TextUtils.isEmpty(textAttribute.getIntent())) {\n                    final ClickableSpan clickableSpan = new ClickableSpan() {\n                        @Override\n                        public void onClick(@NonNull final View widget) {\n                            handleDeepLink(textAttribute.getIntent());\n                        }\n                    };\n                    sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n            }\n        }\n        binding.tvMessage.setText(sb);\n    }\n\n    @Override\n    protected boolean allowMessageDirectionGravity() {\n        return false;\n    }\n\n    @Override\n    protected boolean showUserDetailsInGroup() {\n        return false;\n    }\n\n    @Override\n    protected boolean showMessageInfo() {\n        return false;\n    }\n\n    @Override\n    protected boolean allowLongClick() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemAnimatedMediaViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;\nimport awais.instagrabber.repositories.responses.AnimatedMediaImages;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmAnimatedMediaBinding binding;\n\n    public DirectItemAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                             @NonNull final LayoutDmAnimatedMediaBinding binding,\n                                             final User currentUser,\n                                             final DirectThread thread,\n                                             final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        final DirectItemAnimatedMedia animatedMediaModel = item.getAnimatedMedia();\n        final AnimatedMediaImages images = animatedMediaModel.getImages();\n        if (images == null) return;\n        final AnimatedMediaFixedHeight fixedHeight = images.getFixedHeight();\n        if (fixedHeight == null) return;\n        final String url = fixedHeight.getWebp();\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                fixedHeight.getHeight(),\n                fixedHeight.getWidth(),\n                mediaImageMaxHeight,\n                mediaImageMaxWidth\n        );\n        binding.ivAnimatedMessage.setVisibility(View.VISIBLE);\n        final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();\n        final int width = widthHeight.first;\n        final int height = widthHeight.second;\n        layoutParams.width = width;\n        layoutParams.height = height;\n        binding.ivAnimatedMessage.requestLayout();\n        binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()\n                                                      .setUri(url)\n                                                      .setAutoPlayAnimations(true)\n                                                      .build());\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        return ImmutableList.of(\n                new DirectItemContextMenu.MenuItem(R.id.detail, R.string.dms_inbox_giphy, item -> {\n                    Utils.openURL(itemView.getContext(), \"https://giphy.com/gifs/\" + item.getAnimatedMedia().getId());\n                    return null;\n                })\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemDefaultViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmTextBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\n\npublic class DirectItemDefaultViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmTextBinding binding;\n\n    public DirectItemDefaultViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                       @NonNull final LayoutDmTextBinding binding,\n                                       final User currentUser,\n                                       final DirectThread thread,\n                                       final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final Context context = itemView.getContext();\n        binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));\n    }\n\n    @Override\n    protected boolean showBackground() {\n        return true;\n    }\n\n    @Override\n    protected boolean allowLongClick() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLikeViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmLikeBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\n\npublic class DirectItemLikeViewHolder extends DirectItemViewHolder {\n\n    public DirectItemLikeViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                    @NonNull final LayoutDmLikeBinding binding,\n                                    final User currentUser,\n                                    final DirectThread thread,\n                                    final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {}\n\n    @Override\n    protected boolean canForward() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLinkViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmLinkBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemLink;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemLinkContext;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectItemLinkViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmLinkBinding binding;\n\n    public DirectItemLinkViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                    final LayoutDmLinkBinding binding,\n                                    final User currentUser,\n                                    final DirectThread thread,\n                                    final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        final int width = windowWidth - margin - dmRadiusSmall;\n        final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();\n        layoutParams.width = width;\n        binding.preview.requestLayout();\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        final DirectItemLink link = item.getLink();\n        if (link == null) return;\n        final DirectItemLinkContext linkContext = link.getLinkContext();\n        if (linkContext == null) return;\n        final String linkImageUrl = linkContext.getLinkImageUrl();\n        if (TextUtils.isEmpty(linkImageUrl)) {\n            binding.preview.setVisibility(View.GONE);\n        } else {\n            binding.preview.setVisibility(View.VISIBLE);\n            binding.preview.setImageURI(linkImageUrl);\n        }\n        if (TextUtils.isEmpty(linkContext.getLinkTitle())) {\n            binding.title.setVisibility(View.GONE);\n        } else {\n            binding.title.setVisibility(View.VISIBLE);\n            binding.title.setText(linkContext.getLinkTitle());\n        }\n        if (TextUtils.isEmpty(linkContext.getLinkSummary())) {\n            binding.summary.setVisibility(View.GONE);\n        } else {\n            binding.summary.setVisibility(View.VISIBLE);\n            binding.summary.setText(linkContext.getLinkSummary());\n        }\n        if (TextUtils.isEmpty(linkContext.getLinkUrl())) {\n            binding.url.setVisibility(View.GONE);\n        } else {\n            binding.url.setVisibility(View.VISIBLE);\n            binding.url.setText(linkContext.getLinkUrl());\n        }\n        binding.text.setText(link.getText());\n        setupListeners(linkContext);\n    }\n\n    private void setupListeners(final DirectItemLinkContext linkContext) {\n        setupRamboTextListeners(binding.text);\n        final View.OnClickListener onClickListener = v -> openURL(linkContext.getLinkUrl());\n        binding.preview.setOnClickListener(onClickListener);\n        // binding.preview.setOnLongClickListener(v -> itemView.performLongClick());\n        binding.title.setOnClickListener(onClickListener);\n        binding.summary.setOnClickListener(onClickListener);\n        binding.url.setOnClickListener(onClickListener);\n    }\n\n    @Override\n    protected boolean showBackground() {\n        return true;\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        return ImmutableList.of(\n                new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy, item -> {\n                    final DirectItemLink link = item.getLink();\n                    if (link == null || TextUtils.isEmpty(link.getText())) return null;\n                    Utils.copyText(itemView.getContext(), link.getText());\n                    return null;\n                })\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaShareViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.text.TextUtils;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmMediaShareBinding;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Caption;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemClip;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemFelixShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectItemMediaShareViewHolder extends DirectItemViewHolder {\n    private static final String TAG = DirectItemMediaShareViewHolder.class.getSimpleName();\n\n    private final LayoutDmMediaShareBinding binding;\n    private final RoundingParams incomingRoundingParams;\n    private final RoundingParams outgoingRoundingParams;\n    private DirectItemType itemType;\n    private Caption caption;\n\n    public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                          @NonNull final LayoutDmMediaShareBinding binding,\n                                          final User currentUser,\n                                          final DirectThread thread,\n                                          final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);\n        outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        binding.topBg.setBackgroundResource(messageDirection == MessageDirection.INCOMING\n                                            ? R.drawable.bg_media_share_top_incoming\n                                            : R.drawable.bg_media_share_top_outgoing);\n        Media media = getMedia(item);\n        if (media == null) return;\n        itemView.post(() -> {\n            setupUser(media);\n            setupCaption(media);\n        });\n        final int index;\n        final Media toDisplay;\n        final MediaItemType mediaType = media.getType();\n        switch (mediaType) {\n            case MEDIA_TYPE_SLIDER:\n                toDisplay = media.getCarouselMedia().stream()\n                        .filter(m -> media.getCarouselShareChildMediaId() != null &&\n                                media.getCarouselShareChildMediaId().equals(m.getId()))\n                        .findAny()\n                        .orElse(media.getCarouselMedia().get(0));\n                index = media.getCarouselMedia().indexOf(toDisplay);\n                break;\n            default:\n                toDisplay = media;\n                index = 0;\n        }\n        itemView.post(() -> {\n            setupTypeIndicator(mediaType);\n            setupPreview(toDisplay, messageDirection);\n        });\n        itemView.setOnClickListener(v -> openMedia(media, index));\n    }\n\n    private void setupTypeIndicator(final MediaItemType mediaType) {\n        final boolean showTypeIcon = mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER;\n        if (!showTypeIcon) {\n            binding.typeIcon.setVisibility(View.GONE);\n        } else {\n            binding.typeIcon.setVisibility(View.VISIBLE);\n            binding.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_VIDEO\n                                              ? R.drawable.ic_video_24\n                                              : R.drawable.ic_checkbox_multiple_blank_stroke);\n        }\n    }\n\n    private void setupPreview(@NonNull final Media media,\n                              final MessageDirection messageDirection) {\n        final String url = ResponseBodyUtils.getThumbUrl(media);\n        if (Objects.equals(url, binding.mediaPreview.getTag())) {\n            return;\n        }\n        final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;\n        binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())\n                                                  .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)\n                                                  .setRoundingParams(roundingParams)\n                                                  .build());\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                media.getOriginalHeight(),\n                media.getOriginalWidth(),\n                mediaImageMaxHeight,\n                mediaImageMaxWidth\n        );\n        final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();\n        layoutParams.width = widthHeight.first;\n        layoutParams.height = widthHeight.second;\n        binding.mediaPreview.requestLayout();\n        binding.mediaPreview.setTag(url);\n        binding.mediaPreview.setImageURI(url);\n    }\n\n    private void setupCaption(@NonNull final Media media) {\n        caption = media.getCaption();\n        if (caption != null) {\n            binding.caption.setVisibility(View.VISIBLE);\n            binding.caption.setText(caption.getText());\n            binding.caption.setEllipsize(TextUtils.TruncateAt.END);\n            binding.caption.setMaxLines(2);\n        } else {\n            binding.caption.setVisibility(View.GONE);\n        }\n    }\n\n    private void setupUser(@NonNull final Media media) {\n        final User user = media.getUser();\n        if (user != null) {\n            binding.username.setVisibility(View.VISIBLE);\n            binding.profilePic.setVisibility(View.VISIBLE);\n            binding.username.setText(user.getUsername());\n            binding.profilePic.setImageURI(user.getProfilePicUrl());\n        } else {\n            binding.username.setVisibility(View.GONE);\n            binding.profilePic.setVisibility(View.GONE);\n        }\n    }\n\n    @Nullable\n    private Media getMedia(@NonNull final DirectItem item) {\n        Media media = null;\n        itemType = item.getItemType();\n        if (itemType == DirectItemType.MEDIA_SHARE) {\n            media = item.getMediaShare();\n        } else if (itemType == DirectItemType.CLIP) {\n            final DirectItemClip clip = item.getClip();\n            if (clip == null) return null;\n            media = clip.getClip();\n        } else if (itemType == DirectItemType.FELIX_SHARE) {\n            final DirectItemFelixShare felixShare = item.getFelixShare();\n            if (felixShare == null) return null;\n            media = felixShare.getVideo();\n        }\n        return media;\n    }\n\n    @Override\n    protected int getReactionsTranslationY() {\n        return reactionTranslationYType2;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        if (itemType != null && (itemType == DirectItemType.CLIP || itemType == DirectItemType.FELIX_SHARE)) {\n            return ItemTouchHelper.ACTION_STATE_IDLE;\n        }\n        return super.getSwipeDirection();\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();\n        if (caption != null && !TextUtils.isEmpty(caption.getText())) {\n            builder.add(new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy_caption, item -> {\n                Utils.copyText(itemView.getContext(), caption.getText());\n                return null;\n            }));\n        }\n        return builder.build();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\n\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmMediaBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic class DirectItemMediaViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmMediaBinding binding;\n    private final RoundingParams incomingRoundingParams;\n    private final RoundingParams outgoingRoundingParams;\n\n    public DirectItemMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                     @NonNull final LayoutDmMediaBinding binding,\n                                     final User currentUser,\n                                     final DirectThread thread,\n                                     final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);\n        outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;\n        binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())\n                                                  .setRoundingParams(roundingParams)\n                                                  .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)\n                                                  .build());\n        final Media media = directItemModel.getMedia();\n        itemView.setOnClickListener(v -> openMedia(media, -1));\n        final MediaItemType modelMediaType = media.getType();\n        binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER\n                                       ? View.VISIBLE\n                                       : View.GONE);\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                media.getOriginalHeight(),\n                media.getOriginalWidth(),\n                mediaImageMaxHeight,\n                mediaImageMaxWidth\n        );\n        final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();\n        final int width = widthHeight.first;\n        layoutParams.width = width;\n        layoutParams.height = widthHeight.second;\n        binding.mediaPreview.requestLayout();\n        binding.bgTime.getLayoutParams().width = width;\n        binding.bgTime.requestLayout();\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);\n        binding.mediaPreview.setImageURI(thumbUrl);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemPlaceholderViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmStoryShareBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\n\npublic class DirectItemPlaceholderViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmStoryShareBinding binding;\n\n    public DirectItemPlaceholderViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                           final LayoutDmStoryShareBinding binding,\n                                           final User currentUser,\n                                           final DirectThread thread,\n                                           final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        binding.shareInfo.setText(directItemModel.getPlaceholder().getTitle());\n        binding.text.setVisibility(View.VISIBLE);\n        binding.text.setText(directItemModel.getPlaceholder().getMessage());\n        binding.ivMediaPreview.setVisibility(View.GONE);\n        binding.typeIcon.setVisibility(View.GONE);\n    }\n\n    @Override\n    protected boolean allowLongClick() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\nimport com.facebook.drawee.view.SimpleDraweeView;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmProfileBinding;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemProfileViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmProfileBinding binding;\n    private final ImmutableList<SimpleDraweeView> previewViews;\n\n    public DirectItemProfileViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                       @NonNull final LayoutDmProfileBinding binding,\n                                       final User currentUser,\n                                       final DirectThread thread,\n                                       final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n        previewViews = ImmutableList.of(\n                binding.preview1,\n                binding.preview2,\n                binding.preview3,\n                binding.preview4,\n                binding.preview5,\n                binding.preview6\n        );\n    }\n\n    @Override\n    public void bindItem(@NonNull final DirectItem item,\n                         final MessageDirection messageDirection) {\n        binding.getRoot().setBackgroundResource(messageDirection == MessageDirection.INCOMING\n                                                ? R.drawable.bg_speech_bubble_incoming\n                                                : R.drawable.bg_speech_bubble_outgoing);\n        if (item.getItemType() == DirectItemType.PROFILE) {\n            setProfile(item);\n        } else if (item.getItemType() == DirectItemType.LOCATION) {\n            setLocation(item);\n        } else {\n            return;\n        }\n        for (final SimpleDraweeView previewView : previewViews) {\n            previewView.setImageURI((String) null);\n        }\n        final List<Media> previewMedias = item.getPreviewMedias();\n        if (previewMedias == null || previewMedias.size() <= 0) {\n            binding.firstRow.setVisibility(View.GONE);\n            binding.secondRow.setVisibility(View.GONE);\n            return;\n        }\n        final Resources resources = itemView.getResources();\n        if (previewMedias.size() <= 3) {\n            binding.firstRow.setVisibility(View.VISIBLE);\n            binding.secondRow.setVisibility(View.GONE);\n            binding.preview1.setHierarchy(new GenericDraweeHierarchyBuilder(resources)\n                    .setRoundingParams(RoundingParams.fromCornersRadii(0, 0, 0, dmRadius))\n                    .build());\n            binding.preview3.setHierarchy(new GenericDraweeHierarchyBuilder(resources)\n                    .setRoundingParams(RoundingParams.fromCornersRadii(0, 0, dmRadius, 0))\n                    .build());\n        }\n        if (previewMedias.size() > 3) {\n            binding.preview4.setHierarchy(new GenericDraweeHierarchyBuilder(resources)\n                    .setRoundingParams(RoundingParams.fromCornersRadii(0, 0, 0, dmRadius))\n                    .build());\n            binding.preview6.setHierarchy(new GenericDraweeHierarchyBuilder(resources)\n                    .setRoundingParams(RoundingParams.fromCornersRadii(0, 0, dmRadius, 0))\n                    .build());\n        }\n        for (int i = 0; i < previewMedias.size(); i++) {\n            final Media previewMedia = previewMedias.get(i);\n            if (previewMedia == null) continue;\n            final String url = ResponseBodyUtils.getThumbUrl(previewMedia);\n            if (url == null) continue;\n            previewViews.get(i).setImageURI(url);\n        }\n    }\n\n    private void setProfile(@NonNull final DirectItem item) {\n        final User profile = item.getProfile();\n        if (profile == null) return;\n        binding.profilePic.setImageURI(profile.getProfilePicUrl());\n        binding.username.setText(profile.getUsername());\n        final String fullName = profile.getFullName();\n        if (!TextUtils.isEmpty(fullName)) {\n            binding.fullName.setVisibility(View.VISIBLE);\n            binding.fullName.setText(fullName);\n        } else {\n            binding.fullName.setVisibility(View.GONE);\n        }\n        binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE);\n        itemView.setOnClickListener(v -> openProfile(profile.getUsername()));\n    }\n\n    private void setLocation(@NonNull final DirectItem item) {\n        final Location location = item.getLocation();\n        if (location == null) return;\n        binding.profilePic.setVisibility(View.GONE);\n        binding.username.setText(location.getName());\n        final String address = location.getAddress();\n        if (!TextUtils.isEmpty(address)) {\n            binding.fullName.setText(address);\n            binding.fullName.setVisibility(View.VISIBLE);\n        } else {\n            binding.fullName.setVisibility(View.GONE);\n        }\n        binding.isVerified.setVisibility(View.GONE);\n        itemView.setOnClickListener(v -> openLocation(location.getPk()));\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmRavenMediaBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.models.enums.RavenMediaViewMode;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmRavenMediaBinding binding;\n    private final int maxWidth;\n\n    public DirectItemRavenMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                          @NonNull final LayoutDmRavenMediaBinding binding,\n                                          final User currentUser,\n                                          final DirectThread thread,\n                                          final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        maxWidth = windowWidth - margin - dmRadiusSmall;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final DirectItemVisualMedia visualMedia = directItemModel.getVisualMedia();\n        final Media media = visualMedia.getMedia();\n        if (media == null) return;\n        setExpiryInfo(visualMedia);\n        setPreview(visualMedia, messageDirection);\n        final boolean expired = TextUtils.isEmpty(media.getId());\n        if (expired) return;\n        itemView.setOnClickListener(v -> openMedia(media, -1));\n        /*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||\n                TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;\n\n        RavenExpiringMediaActionSummary mediaActionSummary = null;\n        if (visualMedia != null) {\n            mediaActionSummary = visualMedia.getExpiringMediaActionSummary();\n        }\n        binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE);\n\n        int textRes = R.string.dms_inbox_raven_media_unknown;\n        if (isExpired) textRes = R.string.dms_inbox_raven_media_expired;\n\n        if (!isExpired) {\n            if (mediaActionSummary != null) {\n                final ActionType expiringMediaType = mediaActionSummary.getType();\n\n                if (expiringMediaType == ActionType.DELIVERED)\n                    textRes = R.string.dms_inbox_raven_media_delivered;\n                else if (expiringMediaType == ActionType.SENT)\n                    textRes = R.string.dms_inbox_raven_media_sent;\n                else if (expiringMediaType == ActionType.OPENED)\n                    textRes = R.string.dms_inbox_raven_media_opened;\n                else if (expiringMediaType == ActionType.REPLAYED)\n                    textRes = R.string.dms_inbox_raven_media_replayed;\n                else if (expiringMediaType == ActionType.SENDING)\n                    textRes = R.string.dms_inbox_raven_media_sending;\n                else if (expiringMediaType == ActionType.BLOCKED)\n                    textRes = R.string.dms_inbox_raven_media_blocked;\n                else if (expiringMediaType == ActionType.SUGGESTED)\n                    textRes = R.string.dms_inbox_raven_media_suggested;\n                else if (expiringMediaType == ActionType.SCREENSHOT)\n                    textRes = R.string.dms_inbox_raven_media_screenshot;\n                else if (expiringMediaType == ActionType.CANNOT_DELIVER)\n                    textRes = R.string.dms_inbox_raven_media_cant_deliver;\n            }\n\n            final RavenMediaViewMode ravenMediaViewMode = visualMedia.getViewType();\n            if (ravenMediaViewMode == RavenMediaViewMode.PERMANENT || ravenMediaViewMode == RavenMediaViewMode.REPLAYABLE) {\n                final MediaItemType mediaType = mediaModel.getMediaType();\n                textRes = -1;\n                binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER\n                                               ? View.VISIBLE\n                                               : View.GONE);\n                final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                        mediaModel.getHeight(),\n                        mediaModel.getWidth(),\n                        maxHeight,\n                        maxWidth\n                );\n                final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();\n                layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;\n                layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;\n                binding.ivMediaPreview.requestLayout();\n                binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());\n            }\n        }\n        if (textRes != -1) {\n            binding.tvMessage.setText(context.getText(textRes));\n            binding.tvMessage.setVisibility(View.VISIBLE);\n        }*/\n    }\n\n    private void setExpiryInfo(final DirectItemVisualMedia visualMedia) {\n        final Media media = visualMedia.getMedia();\n        final RavenMediaViewMode viewMode = visualMedia.getViewMode();\n        if (viewMode != RavenMediaViewMode.PERMANENT) {\n            final MediaItemType mediaType = media.getType();\n            final boolean expired = TextUtils.isEmpty(media.getId());\n            final int info;\n            switch (mediaType) {\n                case MEDIA_TYPE_IMAGE:\n                    if (expired) {\n                        info = R.string.raven_image_expired;\n                        break;\n                    }\n                    info = R.string.raven_image_info;\n                    break;\n                case MEDIA_TYPE_VIDEO:\n                    if (expired) {\n                        info = R.string.raven_video_expired;\n                        break;\n                    }\n                    info = R.string.raven_video_info;\n                    break;\n                default:\n                    if (expired) {\n                        info = R.string.raven_msg_expired;\n                        break;\n                    }\n                    info = R.string.raven_msg_info;\n                    break;\n            }\n            binding.expiryInfo.setVisibility(View.VISIBLE);\n            binding.expiryInfo.setText(info);\n            return;\n        }\n        binding.expiryInfo.setVisibility(View.GONE);\n    }\n\n    private void setPreview(final DirectItemVisualMedia visualMedia,\n                            final MessageDirection messageDirection) {\n        final Media media = visualMedia.getMedia();\n        final boolean expired = TextUtils.isEmpty(media.getId());\n        if (expired) {\n            binding.preview.setVisibility(View.GONE);\n            binding.typeIcon.setVisibility(View.GONE);\n            return;\n        }\n        final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING\n                                              ? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)\n                                              : RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);\n        binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())\n                                             .setRoundingParams(roundingParams)\n                                             .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)\n                                             .build());\n        final MediaItemType modelMediaType = media.getType();\n        binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER\n                                       ? View.VISIBLE\n                                       : View.GONE);\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                media.getOriginalHeight(),\n                media.getOriginalWidth(),\n                mediaImageMaxHeight,\n                maxWidth\n        );\n        final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();\n        layoutParams.width = widthHeight.first;\n        layoutParams.height = widthHeight.second;\n        binding.preview.requestLayout();\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);\n        binding.preview.setImageURI(thumbUrl);\n    }\n\n    @Override\n    protected boolean allowLongClick() {\n        return false; // disabling until confirmed\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemReelShareViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.Gravity;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmReelShareBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectItemReelShareViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmReelShareBinding binding;\n    private String type;\n\n    public DirectItemReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                         @NonNull final LayoutDmReelShareBinding binding,\n                                         final User currentUser,\n                                         final DirectThread thread,\n                                         final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        final DirectItemReelShare reelShare = item.getReelShare();\n        type = reelShare.getType();\n        if (type == null) return;\n        final boolean isSelf = isSelf(item);\n        final Media media = reelShare.getMedia();\n        if (media == null) return;\n        final User user = media.getUser();\n        if (user == null) return;\n        final boolean expired = media.getType() == null;\n        if (expired) {\n            binding.preview.setVisibility(View.GONE);\n            binding.typeIcon.setVisibility(View.GONE);\n            binding.quoteLine.setVisibility(View.GONE);\n            binding.reaction.setVisibility(View.GONE);\n        } else {\n            binding.preview.setVisibility(View.VISIBLE);\n            binding.typeIcon.setVisibility(View.VISIBLE);\n            binding.quoteLine.setVisibility(View.VISIBLE);\n            binding.reaction.setVisibility(View.VISIBLE);\n        }\n        setGravity(messageDirection, expired);\n        if (type.equals(\"reply\")) {\n            setReply(messageDirection, reelShare, isSelf);\n        }\n        if (type.equals(\"reaction\")) {\n            setReaction(messageDirection, reelShare, isSelf, expired);\n        }\n        if (type.equals(\"mention\")) {\n            setMention(isSelf);\n        }\n        if (!expired) {\n            setPreview(media);\n            itemView.setOnClickListener(v -> openMedia(media, -1));\n        }\n    }\n\n    private void setGravity(final MessageDirection messageDirection, final boolean expired) {\n        final boolean isIncoming = messageDirection == MessageDirection.INCOMING;\n        binding.shareInfo.setGravity(isIncoming ? Gravity.START : Gravity.END);\n        if (!expired) {\n            binding.quoteLine.setVisibility(isIncoming ? View.VISIBLE : View.GONE);\n            binding.quoteLineEnd.setVisibility(isIncoming ? View.GONE : View.VISIBLE);\n        }\n        final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();\n        layoutParams.horizontalBias = isIncoming ? 0 : 1;\n        final ConstraintLayout.LayoutParams messageLayoutParams = (ConstraintLayout.LayoutParams) binding.message.getLayoutParams();\n        messageLayoutParams.startToStart = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;\n        messageLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;\n        messageLayoutParams.setMarginStart(isIncoming ? messageInfoPaddingSmall : 0);\n        messageLayoutParams.setMarginEnd(isIncoming ? 0 : messageInfoPaddingSmall);\n        final ConstraintLayout.LayoutParams reactionLayoutParams = (ConstraintLayout.LayoutParams) binding.reaction.getLayoutParams();\n        final int previewId = binding.preview.getId();\n        if (isIncoming) {\n            reactionLayoutParams.startToEnd = previewId;\n            reactionLayoutParams.endToEnd = previewId;\n            reactionLayoutParams.startToStart = ConstraintLayout.LayoutParams.UNSET;\n            reactionLayoutParams.endToStart = ConstraintLayout.LayoutParams.UNSET;\n        } else {\n            reactionLayoutParams.startToStart = previewId;\n            reactionLayoutParams.endToStart = previewId;\n            reactionLayoutParams.startToEnd = ConstraintLayout.LayoutParams.UNSET;\n            reactionLayoutParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;\n        }\n    }\n\n    private void setReply(final MessageDirection messageDirection,\n                          final DirectItemReelShare reelShare,\n                          final boolean isSelf) {\n        final int info = isSelf ? R.string.replied_story_outgoing : R.string.replied_story_incoming;\n        binding.shareInfo.setText(info);\n        binding.reaction.setVisibility(View.GONE);\n        final String text = reelShare.getText();\n        if (TextUtils.isEmpty(text)) {\n            binding.message.setVisibility(View.GONE);\n            return;\n        }\n        setMessage(messageDirection, text);\n    }\n\n    private void setReaction(final MessageDirection messageDirection,\n                             final DirectItemReelShare reelShare,\n                             final boolean isSelf,\n                             final boolean expired) {\n        final int info = isSelf ? R.string.reacted_story_outgoing : R.string.reacted_story_incoming;\n        binding.shareInfo.setText(info);\n        binding.message.setVisibility(View.GONE);\n        final String text = reelShare.getText();\n        if (TextUtils.isEmpty(text)) {\n            binding.reaction.setVisibility(View.GONE);\n            return;\n        }\n        if (expired) {\n            setMessage(messageDirection, text);\n            return;\n        }\n        binding.reaction.setVisibility(View.VISIBLE);\n        binding.reaction.setText(text);\n    }\n\n    private void setMention(final boolean isSelf) {\n        final int info = isSelf ? R.string.mentioned_story_outgoing : R.string.mentioned_story_incoming;\n        binding.shareInfo.setText(info);\n        binding.message.setVisibility(View.GONE);\n        binding.reaction.setVisibility(View.GONE);\n    }\n\n    private void setMessage(final MessageDirection messageDirection, final String text) {\n        binding.message.setVisibility(View.VISIBLE);\n        binding.message.setBackgroundResource(messageDirection == MessageDirection.INCOMING\n                                              ? R.drawable.bg_speech_bubble_incoming\n                                              : R.drawable.bg_speech_bubble_outgoing);\n        binding.message.setText(text);\n    }\n\n    private void setPreview(final Media media) {\n        final MediaItemType mediaType = media.getType();\n        if (mediaType == null) return;\n        binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER\n                                       ? View.VISIBLE : View.GONE);\n        final RoundingParams roundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadiusSmall, dmRadiusSmall, dmRadiusSmall);\n        binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())\n                                             .setRoundingParams(roundingParams)\n                                             .build());\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(media);\n        binding.preview.setImageURI(thumbUrl);\n    }\n\n    @Override\n    protected boolean canForward() {\n        return false;\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();\n        if (type != null && type.equals(\"reply\")) {\n            builder.add(new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy_reply, item -> {\n                final DirectItemReelShare reelShare = item.getReelShare();\n                if (reelShare == null) return null;\n                final String text = reelShare.getText();\n                if (TextUtils.isEmpty(text)) return null;\n                Utils.copyText(itemView.getContext(), text);\n                return null;\n            }));\n        }\n        return builder.build();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemStoryShareViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.content.res.Resources;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.RoundingParams;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmStoryShareBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemStoryShareViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmStoryShareBinding binding;\n\n    public DirectItemStoryShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                          @NonNull final LayoutDmStoryShareBinding binding,\n                                          final User currentUser,\n                                          final DirectThread thread,\n                                          final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        final Resources resources = itemView.getResources();\n        int format = R.string.story_share;\n        final String reelType = item.getStoryShare().getReelType();\n        if (reelType == null || item.getStoryShare().getMedia() == null) {\n            setExpiredStoryInfo(item);\n            return;\n        }\n        if (reelType.equals(\"highlight_reel\")) {\n            format = R.string.story_share_highlight;\n        }\n        final User user = item.getStoryShare().getMedia().getUser();\n        final String info = resources.getString(format, user != null ? user.getUsername() : \"\");\n        binding.shareInfo.setText(info);\n        binding.text.setVisibility(View.GONE);\n        binding.ivMediaPreview.setController(null);\n        final DirectItemStoryShare storyShare = item.getStoryShare();\n        if (storyShare == null) return;\n        setText(storyShare);\n        final Media media = storyShare.getMedia();\n        setupPreview(messageDirection, media);\n        itemView.setOnClickListener(v -> openStory(storyShare));\n    }\n\n    private void setupPreview(final MessageDirection messageDirection, final Media storyShareMedia) {\n        final MediaItemType mediaType = storyShareMedia.getType();\n        binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);\n        final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING\n                                              ? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)\n                                              : RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);\n        binding.ivMediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())\n                                                    .setRoundingParams(roundingParams)\n                                                    .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)\n                                                    .build());\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                storyShareMedia.getOriginalHeight(),\n                storyShareMedia.getOriginalWidth(),\n                mediaImageMaxHeight,\n                mediaImageMaxWidth\n        );\n        final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();\n        layoutParams.width = widthHeight.first;\n        layoutParams.height = widthHeight.second;\n        binding.ivMediaPreview.requestLayout();\n        final String thumbUrl = ResponseBodyUtils.getThumbUrl(storyShareMedia);\n        binding.ivMediaPreview.setImageURI(thumbUrl);\n    }\n\n    private void setText(final DirectItemStoryShare storyShare) {\n        final String text = storyShare.getText();\n        if (!TextUtils.isEmpty(text)) {\n            binding.text.setText(text);\n            binding.text.setVisibility(View.VISIBLE);\n            return;\n        }\n        binding.text.setVisibility(View.GONE);\n    }\n\n    private void setExpiredStoryInfo(final DirectItem item) {\n        binding.shareInfo.setText(item.getStoryShare().getTitle());\n        binding.text.setVisibility(View.VISIBLE);\n        binding.text.setText(item.getStoryShare().getMessage());\n        binding.ivMediaPreview.setVisibility(View.GONE);\n        binding.typeIcon.setVisibility(View.GONE);\n    }\n\n    @Override\n    protected boolean canForward() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemTextViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmTextBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectItemTextViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmTextBinding binding;\n\n    public DirectItemTextViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                    @NonNull final LayoutDmTextBinding binding,\n                                    final User currentUser,\n                                    final DirectThread thread,\n                                    @NonNull final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final String text = directItemModel.getText();\n        if (text == null) return;\n        binding.tvMessage.setText(text);\n        setupRamboTextListeners(binding.tvMessage);\n    }\n\n    @Override\n    protected boolean showBackground() {\n        return true;\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        return ImmutableList.of(\n                new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy, item -> {\n                    if (TextUtils.isEmpty(item.getText())) return null;\n                    Utils.copyText(itemView.getContext(), item.getText());\n                    return null;\n                })\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemVideoCallEventViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.style.ClickableSpan;\nimport android.text.style.ForegroundColorSpan;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmActionLogBinding;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVideoCallEvent;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.TextRange;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemVideoCallEventViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmActionLogBinding binding;\n\n    public DirectItemVideoCallEventViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                              final LayoutDmActionLogBinding binding,\n                                              final User currentUser,\n                                              final DirectThread thread,\n                                              final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final DirectItemVideoCallEvent videoCallEvent = directItemModel.getVideoCallEvent();\n        final String text = videoCallEvent.getDescription();\n        final SpannableStringBuilder sb = new SpannableStringBuilder(text);\n        final List<TextRange> textAttributes = videoCallEvent.getTextAttributes();\n        if (textAttributes != null && !textAttributes.isEmpty()) {\n            for (final TextRange textAttribute : textAttributes) {\n                if (!TextUtils.isEmpty(textAttribute.getColor())) {\n                    final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));\n                    sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n                if (!TextUtils.isEmpty(textAttribute.getIntent())) {\n                    final ClickableSpan clickableSpan = new ClickableSpan() {\n                        @Override\n                        public void onClick(@NonNull final View widget) {\n\n                        }\n                    };\n                    sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n                }\n            }\n        }\n        binding.tvMessage.setMaxLines(1);\n        binding.tvMessage.setText(sb);\n    }\n\n    @Override\n    protected boolean allowMessageDirectionGravity() {\n        return false;\n    }\n\n    @Override\n    protected boolean showUserDetailsInGroup() {\n        return false;\n    }\n\n    @Override\n    protected boolean showMessageInfo() {\n        return false;\n    }\n\n    @Override\n    protected boolean allowLongClick() {\n        return false;\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.annotation.SuppressLint;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.graphics.Point;\nimport android.graphics.drawable.Drawable;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewPropertyAnimator;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.widget.ImageViewCompat;\nimport androidx.recyclerview.widget.ItemTouchHelper;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.transition.TransitionManager;\n\nimport com.google.android.material.transition.MaterialFade;\nimport com.google.common.collect.ImmutableList;\n\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemInternalLongClickListener;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.customviews.DirectItemFrameLayout;\nimport awais.instagrabber.customviews.RamboTextViewV2;\nimport awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback.SwipeableViewHolder;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.DMUtils;\nimport awais.instagrabber.utils.DeepLinkParser;\nimport awais.instagrabber.utils.ResponseBodyUtils;\n\npublic abstract class DirectItemViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {\n    private static final String TAG = DirectItemViewHolder.class.getSimpleName();\n    // private static final List<Integer> THREAD_CHANGING_OPTIONS = ImmutableList.of(R.id.unsend);\n\n    private final LayoutDmBaseBinding binding;\n    private final User currentUser;\n    private final DirectThread thread;\n    private final int groupMessageWidth;\n    private final List<Long> userIds;\n    private final DirectItemCallback callback;\n    private final int reactionAdjustMargin;\n    private final AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();\n\n    protected final int margin;\n    protected final int dmRadius;\n    protected final int dmRadiusSmall;\n    protected final int messageInfoPaddingSmall;\n    protected final int mediaImageMaxHeight;\n    protected final int windowWidth;\n    protected final int mediaImageMaxWidth;\n    protected final int reactionTranslationYType1;\n    protected final int reactionTranslationYType2;\n\n    private boolean selected = false;\n    private DirectItemInternalLongClickListener longClickListener;\n    private DirectItem item;\n    private ViewPropertyAnimator shrinkGrowAnimator;\n    private MessageDirection messageDirection;\n    // private View.OnLayoutChangeListener layoutChangeListener;\n\n    public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,\n                                @NonNull final User currentUser,\n                                @NonNull final DirectThread thread,\n                                @NonNull final DirectItemCallback callback) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.currentUser = currentUser;\n        this.thread = thread;\n        this.callback = callback;\n        userIds = thread.getUsers()\n                        .stream()\n                        .map(User::getPk)\n                        .collect(Collectors.toList());\n        binding.ivProfilePic.setVisibility(thread.isGroup() ? View.VISIBLE : View.GONE);\n        binding.ivProfilePic.setOnClickListener(null);\n        final Resources resources = itemView.getResources();\n        margin = resources.getDimensionPixelSize(R.dimen.dm_message_item_margin);\n        final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_message_item_avatar_size);\n        dmRadius = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius);\n        dmRadiusSmall = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius_small);\n        messageInfoPaddingSmall = resources.getDimensionPixelSize(R.dimen.dm_message_info_padding_small);\n        windowWidth = resources.getDisplayMetrics().widthPixels;\n        mediaImageMaxHeight = resources.getDimensionPixelSize(R.dimen.dm_media_img_max_height);\n        reactionAdjustMargin = resources.getDimensionPixelSize(R.dimen.dm_reaction_adjust_margin);\n        final int groupWidthCorrection = avatarSize + messageInfoPaddingSmall * 3;\n        mediaImageMaxWidth = windowWidth - margin - (thread.isGroup() ? groupWidthCorrection : messageInfoPaddingSmall * 2);\n        // messageInfoPaddingSmall is used cuz it's also 4dp, 1 avatar margin + 2 paddings = 3\n        groupMessageWidth = windowWidth - margin - groupWidthCorrection;\n        reactionTranslationYType1 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_1);\n        reactionTranslationYType2 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_2);\n    }\n\n    public void bind(final int position, final DirectItem item) {\n        if (item == null) return;\n        this.item = item;\n        messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;\n        // Asynchronous binding causes some weird behaviour\n        // itemView.post(() -> bindBase(item, messageDirection, position));\n        // itemView.post(() -> bindItem(item, messageDirection));\n        // itemView.post(() -> setupLongClickListener(position, messageDirection));\n        bindBase(item, messageDirection, position);\n        bindItem(item, messageDirection);\n        setupLongClickListener(position, messageDirection);\n    }\n\n    private void bindBase(@NonNull final DirectItem item, final MessageDirection messageDirection, final int position) {\n        final FrameLayout.LayoutParams containerLayoutParams = (FrameLayout.LayoutParams) binding.container.getLayoutParams();\n        final DirectItemType itemType = item.getItemType() == null ? DirectItemType.UNKNOWN : item.getItemType();\n        setMessageDirectionGravity(messageDirection, containerLayoutParams);\n        setGroupUserDetails(item, messageDirection);\n        setBackground(messageDirection);\n        setMessageInfo(item, messageDirection);\n        switch (itemType) {\n            case REEL_SHARE:\n            case STORY_SHARE: // i think they could have texts?\n//                containerLayoutParams.setMarginStart(0);\n//                containerLayoutParams.setMarginEnd(0);\n            case TEXT:\n            case LINK:\n            case UNKNOWN:\n                binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall);\n                break;\n            default:\n                if (showMessageInfo()) {\n                    binding.messageInfo.setPadding(0, 0, messageInfoPaddingSmall, dmRadiusSmall);\n                }\n        }\n        setupReply(item, messageDirection);\n        setReactions(item, position);\n        if (item.getRepliedToMessage() == null && item.getShowForwardAttribution()) {\n            setForwardInfo(messageDirection);\n        }\n    }\n\n    private void setBackground(final MessageDirection messageDirection) {\n        if (showBackground()) {\n            binding.background.setBackgroundResource(messageDirection == MessageDirection.INCOMING ? R.drawable.bg_speech_bubble_incoming\n                                                                                                   : R.drawable.bg_speech_bubble_outgoing);\n            return;\n        }\n        binding.background.setBackgroundResource(0);\n    }\n\n    private void setGroupUserDetails(final DirectItem item, final MessageDirection messageDirection) {\n        if (showUserDetailsInGroup()) {\n            binding.ivProfilePic.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);\n            binding.tvUsername.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);\n            if (messageDirection == MessageDirection.INCOMING && thread.isGroup()) {\n                final List<User> allUsers = new LinkedList(thread.getUsers());\n                allUsers.addAll(thread.getLeftUsers());\n                final User user = getUser(item.getUserId(), allUsers);\n                if (user != null) {\n                    binding.tvUsername.setText(user.getUsername());\n                    binding.ivProfilePic.setImageURI(user.getProfilePicUrl());\n                }\n                ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.chatMessageLayout.getLayoutParams();\n                layoutParams.matchConstraintMaxWidth = groupMessageWidth;\n                binding.chatMessageLayout.setLayoutParams(layoutParams);\n            }\n            return;\n        }\n        binding.ivProfilePic.setVisibility(View.GONE);\n        binding.tvUsername.setVisibility(View.GONE);\n    }\n\n    private void setMessageDirectionGravity(final MessageDirection messageDirection,\n                                            final FrameLayout.LayoutParams containerLayoutParams) {\n        if (allowMessageDirectionGravity()) {\n            containerLayoutParams.setMarginStart(messageDirection == MessageDirection.OUTGOING ? margin : 0);\n            containerLayoutParams.setMarginEnd(messageDirection == MessageDirection.INCOMING ? margin : 0);\n            containerLayoutParams.gravity = messageDirection == MessageDirection.INCOMING ? Gravity.START : Gravity.END;\n            return;\n        }\n        containerLayoutParams.gravity = Gravity.CENTER;\n    }\n\n    private void setMessageInfo(@NonNull final DirectItem item, final MessageDirection messageDirection) {\n        if (showMessageInfo()) {\n            binding.messageInfo.setVisibility(View.VISIBLE);\n            binding.deliveryStatus.setVisibility(messageDirection == MessageDirection.OUTGOING ? View.VISIBLE : View.GONE);\n            if (item.getDate() != null) {\n                final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);\n                binding.messageTime.setText(dateFormatter.format(item.getDate()));\n            }\n            if (messageDirection == MessageDirection.OUTGOING) {\n                if (item.isPending()) {\n                    binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);\n                } else {\n                    final boolean read = DMUtils.isRead(item,\n                                                        thread.getLastSeenAt(),\n                                                        userIds\n                    );\n                    binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);\n                    ImageViewCompat.setImageTintList(\n                            binding.deliveryStatus,\n                            ColorStateList.valueOf(itemView.getResources().getColor(read ? R.color.blue_500 : R.color.grey_500))\n                    );\n                }\n            }\n            return;\n        }\n        binding.messageInfo.setVisibility(View.GONE);\n    }\n\n    private void setupReply(final DirectItem item, final MessageDirection messageDirection) {\n        if (item.getRepliedToMessage() != null) {\n            final List<User> allUsers = new LinkedList(thread.getUsers());\n            allUsers.addAll(thread.getLeftUsers());\n            setReply(item, messageDirection, allUsers);\n        } else {\n            binding.quoteLine.setVisibility(View.GONE);\n            binding.replyContainer.setVisibility(View.GONE);\n            binding.replyInfo.setVisibility(View.GONE);\n        }\n    }\n\n    private void setReply(final DirectItem item,\n                          final MessageDirection messageDirection,\n                          final List<User> users) {\n        final DirectItem replied = item.getRepliedToMessage();\n        final DirectItemType itemType = replied.getItemType();\n        final Resources resources = itemView.getResources();\n        String text = null;\n        String url = null;\n        switch (itemType) {\n            case TEXT:\n                text = replied.getText();\n                break;\n            case LINK:\n                text = replied.getLink().getText();\n                break;\n            case PLACEHOLDER:\n                text = replied.getPlaceholder().getMessage();\n                break;\n            case MEDIA:\n                url = ResponseBodyUtils.getThumbUrl(replied.getMedia());\n                break;\n            case RAVEN_MEDIA:\n                url = ResponseBodyUtils.getThumbUrl(replied.getVisualMedia().getMedia());\n                break;\n            case VOICE_MEDIA:\n                text = resources.getString(R.string.voice_message);\n                break;\n            case MEDIA_SHARE:\n                Media mediaShare = replied.getMediaShare();\n                if (mediaShare.getType() == MediaItemType.MEDIA_TYPE_SLIDER) {\n                    mediaShare = mediaShare.getCarouselMedia().get(0);\n                }\n                url = ResponseBodyUtils.getThumbUrl(mediaShare);\n                break;\n            case REEL_SHARE:\n                text = replied.getReelShare().getText();\n                break;\n            // Below types cannot be replied to\n            // case LIKE:\n            //     text = \"❤️\";\n            //     break;\n            // case PROFILE:\n            //     text = \"@\" + replied.getProfile().getUsername();\n            //     break;\n            // case CLIP:\n            //     url = ResponseBodyUtils.getThumbUrl(replied.getClip().getClip().getImageVersions2());\n            //     break;\n            // case FELIX_SHARE:\n            //     url = ResponseBodyUtils.getThumbUrl(replied.getFelixShare().getVideo().getImageVersions2());\n            //     break;\n            // case STORY_SHARE:\n            //     final DirectItemMedia media = replied.getStoryShare().getMedia();\n            //     if (media == null) break;\n            //     url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());\n            //     break;\n            // case LOCATION\n        }\n        if (text == null && url == null) {\n            binding.quoteLine.setVisibility(View.GONE);\n            binding.replyContainer.setVisibility(View.GONE);\n            binding.replyInfo.setVisibility(View.GONE);\n            return;\n        }\n        setReplyGravity(messageDirection);\n        final String info = setReplyInfo(item, replied, users, resources);\n        binding.replyInfo.setVisibility(View.VISIBLE);\n        binding.replyInfo.setText(info);\n        binding.quoteLine.setVisibility(View.VISIBLE);\n        binding.replyContainer.setVisibility(View.VISIBLE);\n        if (url != null) {\n            binding.replyText.setVisibility(View.GONE);\n            binding.replyImage.setVisibility(View.VISIBLE);\n            binding.replyImage.setImageURI(url);\n            return;\n        }\n        binding.replyImage.setVisibility(View.GONE);\n        final Drawable background = binding.replyText.getBackground().mutate();\n        background.setTint(replied.getUserId() != currentUser.getPk()\n                           ? resources.getColor(R.color.grey_600)\n                           : resources.getColor(R.color.deep_purple_400));\n        binding.replyText.setBackgroundDrawable(background);\n        binding.replyText.setVisibility(View.VISIBLE);\n        binding.replyText.setText(text);\n    }\n\n    private String setReplyInfo(final DirectItem item,\n                                final DirectItem replied,\n                                final List<User> users,\n                                final Resources resources) {\n        final long repliedToUserId = replied.getUserId();\n        if (repliedToUserId == item.getUserId() && item.getUserId() == currentUser.getPk()) {\n            // User replied to own message\n            return resources.getString(R.string.replied_to_yourself);\n        }\n        if (repliedToUserId == item.getUserId()) {\n            // opposite user replied to their own message\n            return resources.getString(R.string.replied_to_themself);\n        }\n        final User user = getUser(repliedToUserId, users);\n        final String repliedToUsername = user != null ? user.getUsername() : \"\";\n        if (item.getUserId() == currentUser.getPk()) {\n            return thread.isGroup()\n                   ? resources.getString(R.string.replied_you_group, repliedToUsername)\n                   : resources.getString(R.string.replied_you);\n        }\n        if (repliedToUserId == currentUser.getPk()) {\n            return resources.getString(R.string.replied_to_you);\n        }\n        return resources.getString(R.string.replied_group, repliedToUsername);\n    }\n\n    private void setForwardInfo(final MessageDirection direction) {\n        binding.replyInfo.setVisibility(View.VISIBLE);\n        binding.replyInfo.setText(direction == MessageDirection.OUTGOING ? R.string.forward_outgoing : R.string.forward_incoming);\n    }\n\n    private void setReplyGravity(final MessageDirection messageDirection) {\n        final boolean isIncoming = messageDirection == MessageDirection.INCOMING;\n        final ConstraintLayout.LayoutParams quoteLineLayoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();\n        final ConstraintLayout.LayoutParams replyContainerLayoutParams = (ConstraintLayout.LayoutParams) binding.replyContainer.getLayoutParams();\n        final ConstraintLayout.LayoutParams replyInfoLayoutParams = (ConstraintLayout.LayoutParams) binding.replyInfo.getLayoutParams();\n        final int profilePicId = binding.ivProfilePic.getId();\n        final int replyContainerId = binding.replyContainer.getId();\n        final int quoteLineId = binding.quoteLine.getId();\n        quoteLineLayoutParams.startToEnd = isIncoming ? profilePicId : replyContainerId;\n        quoteLineLayoutParams.endToStart = isIncoming ? replyContainerId : ConstraintLayout.LayoutParams.UNSET;\n        quoteLineLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;\n        replyContainerLayoutParams.startToEnd = isIncoming ? quoteLineId : profilePicId;\n        replyContainerLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;\n        replyContainerLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;\n        replyInfoLayoutParams.startToEnd = isIncoming ? quoteLineId : ConstraintLayout.LayoutParams.UNSET;\n        replyInfoLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;\n    }\n\n    private void setReactions(final DirectItem item, final int position) {\n        binding.getRoot().post(() -> {\n            MaterialFade materialFade = new MaterialFade();\n            materialFade.addTarget(binding.emojis);\n            TransitionManager.beginDelayedTransition(binding.getRoot(), materialFade);\n            final DirectItemReactions reactions = item.getReactions();\n            final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;\n            if (emojis == null || emojis.isEmpty()) {\n                binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, 0);\n                binding.reactionsWrapper.setVisibility(View.GONE);\n                return;\n            }\n            binding.reactionsWrapper.setVisibility(View.VISIBLE);\n            binding.reactionsWrapper.setTranslationY(getReactionsTranslationY());\n            binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, reactionAdjustMargin);\n            binding.emojis.setEmojis(emojis.stream()\n                                           .map(DirectItemEmojiReaction::getEmoji)\n                                           .collect(Collectors.toList()));\n            // binding.emojis.setEmojis(ImmutableList.of(\"😣\",\n            //                                           \"😖\",\n            //                                           \"😫\",\n            //                                           \"😩\",\n            //                                           \"🥺\",\n            //                                           \"😢\",\n            //                                           \"😭\",\n            //                                           \"😤\",\n            //                                           \"😠\",\n            //                                           \"😡\",\n            //                                           \"🤬\"));\n            binding.emojis.setOnClickListener(v -> callback.onReactionClick(item, position));\n            // final List<DirectUser> reactedUsers = emojis.stream()\n            //                                             .map(DirectItemEmojiReaction::getSenderId)\n            //                                             .distinct()\n            //                                             .map(userId -> getUser(userId, users))\n            //                                             .collect(Collectors.toList());\n            // for (final DirectUser user : reactedUsers) {\n            //     if (user == null) continue;\n            //     final ProfilePicView profilePicView = new ProfilePicView(itemView.getContext());\n            //     profilePicView.setSize(ProfilePicView.Size.TINY);\n            //     profilePicView.setImageURI(user.getProfilePicUrl());\n            //     binding.reactions.addView(profilePicView);\n            // }\n        });\n    }\n\n    protected boolean isSelf(final DirectItem directItem) {\n        return directItem.getUserId() == currentUser.getPk();\n    }\n\n    public void setItemView(final View view) {\n        this.binding.message.addView(view);\n    }\n\n    public abstract void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection);\n\n    @Nullable\n    protected User getUser(final long userId, final List<User> users) {\n        if (userId == currentUser.getPk()) {\n            return currentUser;\n        }\n        if (users == null) return null;\n        for (final User user : users) {\n            if (userId != user.getPk()) continue;\n            return user;\n        }\n        return null;\n    }\n\n    protected boolean allowMessageDirectionGravity() {\n        return true;\n    }\n\n    protected boolean showUserDetailsInGroup() {\n        return true;\n    }\n\n    protected boolean showBackground() {\n        return false;\n    }\n\n    protected boolean showMessageInfo() {\n        return true;\n    }\n\n    protected boolean allowLongClick() {\n        return true;\n    }\n\n    protected boolean allowReaction() {\n        return true;\n    }\n\n    protected boolean canForward() {\n        return true;\n    }\n\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        return null;\n    }\n\n    protected int getReactionsTranslationY() {\n        return reactionTranslationYType1;\n    }\n\n    @CallSuper\n    public void cleanup() {\n        // if (layoutChangeListener != null) {\n        //     binding.container.removeOnLayoutChangeListener(layoutChangeListener);\n        // }\n    }\n\n    protected void setupRamboTextListeners(@NonNull final RamboTextViewV2 textView) {\n        textView.addOnHashtagListener(autoLinkItem -> callback.onHashtagClick(autoLinkItem.getOriginalText().trim()));\n        textView.addOnMentionClickListener(autoLinkItem -> openProfile(autoLinkItem.getOriginalText().trim()));\n        textView.addOnEmailClickListener(autoLinkItem -> callback.onEmailClick(autoLinkItem.getOriginalText().trim()));\n        textView.addOnURLClickListener(autoLinkItem -> openURL(autoLinkItem.getOriginalText().trim()));\n    }\n\n    protected void openProfile(final String username) {\n        callback.onMentionClick(username);\n    }\n\n    protected void openLocation(final long locationId) {\n        callback.onLocationClick(locationId);\n    }\n\n    protected void openURL(final String url) {\n        callback.onURLClick(url);\n    }\n\n    protected void openMedia(final Media media, final int index) {\n        callback.onMediaClick(media, index);\n    }\n\n    protected void openStory(final DirectItemStoryShare storyShare) {\n        callback.onStoryClick(storyShare);\n    }\n\n    protected void handleDeepLink(final String deepLinkText) {\n        if (deepLinkText == null) return;\n        final DeepLinkParser.DeepLink deepLink = DeepLinkParser.parse(deepLinkText);\n        if (deepLink == null) return;\n        switch (deepLink.getType()) {\n            case USER:\n                callback.onMentionClick(deepLink.getValue());\n                break;\n        }\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    private void setupLongClickListener(final int position, final MessageDirection messageDirection) {\n        if (!allowLongClick()) return;\n        binding.getRoot().setOnItemLongClickListener(new DirectItemFrameLayout.OnItemLongClickListener() {\n            @Override\n            public void onLongClickStart(final View view) {\n                itemView.post(() -> shrink());\n            }\n\n            @Override\n            public void onLongClickCancel(final View view) {\n                itemView.post(() -> grow());\n            }\n\n            @Override\n            public void onLongClick(final View view, final float x, final float y) {\n                // if (longClickListener == null) return false;\n                // longClickListener.onLongClick(position, this);\n                itemView.post(() -> grow());\n                setSelected(true);\n                showLongClickOptions(new Point((int) x, (int) y), messageDirection);\n            }\n        });\n    }\n\n    private void showLongClickOptions(final Point location, final MessageDirection messageDirection) {\n        final List<DirectItemContextMenu.MenuItem> longClickOptions = getLongClickOptions();\n        final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();\n        if (longClickOptions != null) {\n            builder.addAll(longClickOptions);\n        }\n        if (canForward()) {\n            builder.add(new DirectItemContextMenu.MenuItem(R.id.forward, R.string.forward));\n        }\n        if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) {\n            builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend));\n        }\n        final boolean showReactions = thread.getInputMode() != 1 && allowReaction();\n        final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build();\n        if (!showReactions && menuItems.isEmpty()) return;\n        final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems);\n        menu.setOnDismissListener(() -> setSelected(false));\n        menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji));\n        menu.setOnOptionSelectListener((itemId, cb) -> callback.onOptionSelect(item, itemId, cb));\n        menu.setOnAddReactionListener(() -> {\n            menu.dismiss();\n            itemView.postDelayed(() -> callback.onAddReactionListener(item), 300);\n        });\n        menu.show(itemView, location);\n    }\n\n    public void setLongClickListener(final DirectItemInternalLongClickListener longClickListener) {\n        this.longClickListener = longClickListener;\n    }\n\n    public void setSelected(final boolean selected) {\n        this.selected = selected;\n    }\n\n    private void shrink() {\n        if (shrinkGrowAnimator != null) {\n            shrinkGrowAnimator.cancel();\n        }\n        shrinkGrowAnimator = itemView.animate()\n                                     .scaleX(0.8f)\n                                     .scaleY(0.8f)\n                                     .setInterpolator(accelerateDecelerateInterpolator)\n                                     .setDuration(ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout());\n        shrinkGrowAnimator.start();\n    }\n\n    private void grow() {\n        if (shrinkGrowAnimator != null) {\n            shrinkGrowAnimator.cancel();\n        }\n        shrinkGrowAnimator = itemView.animate()\n                                     .scaleX(1f)\n                                     .scaleY(1f)\n                                     .setInterpolator(accelerateDecelerateInterpolator)\n                                     .setDuration(200)\n                                     .withEndAction(() -> shrinkGrowAnimator = null);\n        shrinkGrowAnimator.start();\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        if (item == null || messageDirection == null) return ItemTouchHelper.ACTION_STATE_IDLE;\n        return messageDirection == MessageDirection.OUTGOING ? ItemTouchHelper.START : ItemTouchHelper.END;\n    }\n\n    public enum MessageDirection {\n        INCOMING,\n        OUTGOING\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemVoiceMediaViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nimport com.google.android.exoplayer2.MediaItem;\nimport com.google.android.exoplayer2.Player;\nimport com.google.android.exoplayer2.SimpleExoPlayer;\nimport com.google.android.exoplayer2.source.ProgressiveMediaSource;\nimport com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.primitives.Floats;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.customviews.DirectItemContextMenu;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;\nimport awais.instagrabber.repositories.responses.Audio;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.utils.TextUtils;\n\nimport static com.google.android.exoplayer2.C.TIME_UNSET;\n\npublic class DirectItemVoiceMediaViewHolder extends DirectItemViewHolder {\n    private static final String TAG = \"DirectItemVoiceMediaVH\";\n\n    private final LayoutDmVoiceMediaBinding binding;\n    private final DefaultDataSourceFactory dataSourceFactory;\n    private SimpleExoPlayer player;\n    private Handler handler;\n    private Runnable positionChecker;\n    private Player.EventListener listener;\n\n    public DirectItemVoiceMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                          @NonNull final LayoutDmVoiceMediaBinding binding,\n                                          final User currentUser,\n                                          final DirectThread thread,\n                                          final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), \"instagram\");\n        setItemView(binding.getRoot());\n        binding.voiceMedia.getLayoutParams().width = mediaImageMaxWidth;\n    }\n\n    @Override\n    public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {\n        final DirectItemVoiceMedia voiceMedia = directItemModel.getVoiceMedia();\n        if (voiceMedia == null) return;\n        final Media media = voiceMedia.getMedia();\n        if (media == null) return;\n        final Audio audio = media.getAudio();\n        if (audio == null) return;\n        final List<Float> waveformData = audio.getWaveformData();\n        binding.waveformSeekBar.setSample(Floats.toArray(waveformData));\n        binding.waveformSeekBar.setEnabled(false);\n        final String text = String.format(\"%s/%s\", TextUtils.millisToTimeString(0), TextUtils.millisToTimeString(audio.getDuration()));\n        binding.duration.setText(text);\n        final AudioItemState audioItemState = new AudioItemState();\n        player = new SimpleExoPlayer.Builder(itemView.getContext()).build();\n        player.setVolume(1);\n        player.setPlayWhenReady(true);\n        player.setRepeatMode(Player.REPEAT_MODE_OFF);\n        handler = new Handler();\n        final long initialDelay = 0;\n        final long recurringDelay = 60;\n        positionChecker = new Runnable() {\n            @Override\n            public void run() {\n                if (handler != null) {\n                    handler.removeCallbacks(this);\n                }\n                if (player == null) return;\n                final long currentPosition = player.getCurrentPosition();\n                final long duration = player.getDuration();\n                // Log.d(TAG, \"currentPosition: \" + currentPosition + \", duration: \" + duration);\n                if (duration == TIME_UNSET) return;\n                // final float progress = ((float) currentPosition / duration /* * 100 */);\n                final int progress = (int) ((float) currentPosition / duration * 100);\n                // Log.d(TAG, \"progress: \" + progress);\n                final String text = String.format(\"%s/%s\", TextUtils.millisToTimeString(currentPosition), TextUtils.millisToTimeString(duration));\n                binding.duration.setText(text);\n                binding.waveformSeekBar.setProgress(progress);\n                if (handler != null) {\n                    handler.postDelayed(this, recurringDelay);\n                }\n            }\n        };\n        player.addListener(listener = new Player.EventListener() {\n            @Override\n            public void onPlaybackStateChanged(final int state) {\n                if (!audioItemState.isPrepared() && state == Player.STATE_READY) {\n                    binding.playPause.setIconResource(R.drawable.ic_round_pause_24);\n                    audioItemState.setPrepared(true);\n                    binding.playPause.setVisibility(View.VISIBLE);\n                    binding.progressBar.setVisibility(View.GONE);\n                    if (handler != null) {\n                        handler.postDelayed(positionChecker, initialDelay);\n                    }\n                    return;\n                }\n                if (state == Player.STATE_ENDED) {\n                    // binding.waveformSeekBar.setProgressInPercentage(0);\n                    binding.waveformSeekBar.setProgress(0);\n                    binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);\n                    if (handler != null) {\n                        handler.removeCallbacks(positionChecker);\n                    }\n                }\n            }\n\n            @Override\n            public void onPlayerError(final ExoPlaybackException error) {\n                Log.e(TAG, \"onPlayerError: \", error);\n            }\n        });\n        final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);\n        final MediaItem mediaItem = MediaItem.fromUri(audio.getAudioSrc());\n        final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);\n        player.setMediaSource(mediaSource);\n        binding.playPause.setOnClickListener(v -> {\n            if (player == null) return;\n            if (!audioItemState.isPrepared()) {\n                player.prepare();\n                binding.playPause.setVisibility(View.GONE);\n                binding.progressBar.setVisibility(View.VISIBLE);\n                return;\n            }\n            if (player.isPlaying()) {\n                binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);\n                player.pause();\n                return;\n            }\n            binding.playPause.setIconResource(R.drawable.ic_round_pause_24);\n            if (player.getPlaybackState() == Player.STATE_ENDED) {\n                player.seekTo(0);\n                if (handler != null) {\n                    handler.postDelayed(positionChecker, initialDelay);\n                }\n            }\n            binding.waveformSeekBar.setEnabled(true);\n            player.play();\n        });\n    }\n\n    @Override\n    public void cleanup() {\n        super.cleanup();\n        if (handler != null && positionChecker != null) {\n            handler.removeCallbacks(positionChecker);\n            handler = null;\n            positionChecker = null;\n        }\n        if (player != null) {\n            player.release();\n            if (listener != null) {\n                player.removeListener(listener);\n            }\n            player = null;\n        }\n    }\n\n    @Override\n    protected boolean canForward() {\n        return false;\n    }\n\n    @Override\n    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {\n        return ImmutableList.of(\n                new DirectItemContextMenu.MenuItem(R.id.download, R.string.action_download)\n        );\n    }\n\n    private static class AudioItemState {\n        private boolean prepared;\n\n        private AudioItemState() {}\n\n        public boolean isPrepared() {\n            return prepared;\n        }\n\n        public void setPrepared(final boolean prepared) {\n            this.prepared = prepared;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemXmaViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.ItemTouchHelper;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\n\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;\nimport awais.instagrabber.databinding.LayoutDmBaseBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemXma;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.XmaUrlInfo;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\n\npublic class DirectItemXmaViewHolder extends DirectItemViewHolder {\n\n    private final LayoutDmAnimatedMediaBinding binding;\n\n    public DirectItemXmaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,\n                                   @NonNull final LayoutDmAnimatedMediaBinding binding,\n                                   final User currentUser,\n                                   final DirectThread thread,\n                                   final DirectItemCallback callback) {\n        super(baseBinding, currentUser, thread, callback);\n        this.binding = binding;\n        setItemView(binding.getRoot());\n    }\n\n    @Override\n    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {\n        final DirectItemXma xma = item.getXma();\n        final XmaUrlInfo playableUrlInfo = xma.getPlayableUrlInfo();\n        final XmaUrlInfo previewUrlInfo = xma.getPreviewUrlInfo();\n        if (playableUrlInfo == null && previewUrlInfo == null) {\n            binding.ivAnimatedMessage.setController(null);\n            return;\n        }\n        final XmaUrlInfo urlInfo = playableUrlInfo != null ? playableUrlInfo : previewUrlInfo;\n        final String url = urlInfo.getUrl();\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(\n                urlInfo.getHeight(),\n                urlInfo.getWidth(),\n                mediaImageMaxHeight,\n                mediaImageMaxWidth\n        );\n        binding.ivAnimatedMessage.setVisibility(View.VISIBLE);\n        final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();\n        final int width = widthHeight.first;\n        final int height = widthHeight.second;\n        layoutParams.width = width;\n        layoutParams.height = height;\n        binding.ivAnimatedMessage.requestLayout();\n        binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()\n                                                      .setUri(url)\n                                                      .setAutoPlayAnimations(true)\n                                                      .build());\n    }\n\n    @Override\n    public int getSwipeDirection() {\n        return ItemTouchHelper.ACTION_STATE_IDLE;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectPendingUserViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.graphics.drawable.Drawable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.util.Log;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;\nimport awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;\nimport awais.instagrabber.customviews.VerticalImageSpan;\nimport awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectPendingUserViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = DirectPendingUserViewHolder.class.getSimpleName();\n\n    private final LayoutDmPendingUserItemBinding binding;\n    private final PendingUserCallback callback;\n    private final int drawableSize;\n\n    private VerticalImageSpan verifiedSpan;\n\n    public DirectPendingUserViewHolder(@NonNull final LayoutDmPendingUserItemBinding binding,\n                                       final PendingUserCallback callback) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.callback = callback;\n        drawableSize = Utils.convertDpToPx(24);\n    }\n\n    public void bind(final int position, final PendingUser pendingUser) {\n        if (pendingUser == null) return;\n        binding.getRoot().setOnClickListener(v -> {\n            if (callback == null) return;\n            callback.onClick(position, pendingUser);\n        });\n        setUsername(pendingUser);\n        binding.requester.setText(itemView.getResources().getString(R.string.added_by, pendingUser.getRequester()));\n        binding.profilePic.setImageURI(pendingUser.getUser().getProfilePicUrl());\n        if (pendingUser.isInProgress()) {\n            binding.approve.setVisibility(View.GONE);\n            binding.deny.setVisibility(View.GONE);\n            binding.progress.setVisibility(View.VISIBLE);\n            return;\n        }\n        binding.approve.setVisibility(View.VISIBLE);\n        binding.deny.setVisibility(View.VISIBLE);\n        binding.progress.setVisibility(View.GONE);\n        binding.approve.setOnClickListener(v -> {\n            if (callback == null) return;\n            callback.onApprove(position, pendingUser);\n        });\n        binding.deny.setOnClickListener(v -> {\n            if (callback == null) return;\n            callback.onDeny(position, pendingUser);\n        });\n    }\n\n    private void setUsername(final PendingUser pendingUser) {\n        final User user = pendingUser.getUser();\n        final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());\n        if (user.isVerified()) {\n            if (verifiedSpan == null) {\n                final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);\n                if (verifiedDrawable != null) {\n                    final Drawable drawable = verifiedDrawable.mutate();\n                    drawable.setBounds(0, 0, drawableSize, drawableSize);\n                    verifiedSpan = new VerticalImageSpan(drawable);\n                }\n            }\n            try {\n                if (verifiedSpan != null) {\n                    sb.append(\"  \");\n                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"bind: \", e);\n            }\n        }\n        binding.username.setText(sb);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.view.View;\n\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;\nimport awais.instagrabber.customviews.emoji.Emoji;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\npublic class DirectReactionViewHolder extends RecyclerView.ViewHolder {\n    private final LayoutDmUserItemBinding binding;\n    private final long viewerId;\n    private final String itemId;\n    private final OnReactionClickListener onReactionClickListener;\n    private final EmojiParser emojiParser;\n\n    public DirectReactionViewHolder(final LayoutDmUserItemBinding binding,\n                                    final long viewerId,\n                                    final String itemId,\n                                    final OnReactionClickListener onReactionClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.viewerId = viewerId;\n        this.itemId = itemId;\n        this.onReactionClickListener = onReactionClickListener;\n        binding.info.setVisibility(View.GONE);\n        binding.secondaryImage.setVisibility(View.VISIBLE);\n        emojiParser = EmojiParser.Companion.getInstance(itemView.getContext());\n    }\n\n    public void bind(final DirectItemEmojiReaction reaction,\n                     @Nullable final User user) {\n        itemView.setOnClickListener(v -> {\n            if (onReactionClickListener == null) return;\n            onReactionClickListener.onReactionClick(itemId, reaction);\n        });\n        setUser(user);\n        setReaction(reaction);\n    }\n\n    private void setReaction(final DirectItemEmojiReaction reaction) {\n        final Emoji emoji = emojiParser.getEmoji(reaction.getEmoji());\n        if (emoji == null) {\n            binding.secondaryImage.setImageDrawable(null);\n            return;\n        }\n        binding.secondaryImage.setImageDrawable(emoji.getDrawable());\n    }\n\n    private void setUser(final User user) {\n        if (user == null) {\n            binding.fullName.setText(\"\");\n            binding.username.setText(\"\");\n            binding.profilePic.setImageURI((String) null);\n            return;\n        }\n        binding.fullName.setText(user.getFullName());\n        if (user.getPk() == viewerId) {\n            binding.username.setText(R.string.tap_to_remove);\n        } else {\n            binding.username.setText(user.getUsername());\n        }\n        binding.profilePic.setImageURI(user.getProfilePicUrl());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectUserViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.graphics.drawable.Drawable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.util.Log;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;\nimport awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserLongClickListener;\nimport awais.instagrabber.customviews.VerticalImageSpan;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.Utils;\n\npublic class DirectUserViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = DirectUserViewHolder.class.getSimpleName();\n\n    private final LayoutDmUserItemBinding binding;\n    private final OnDirectUserClickListener onClickListener;\n    private final OnDirectUserLongClickListener onLongClickListener;\n    private final int drawableSize;\n\n    private VerticalImageSpan verifiedSpan;\n\n    public DirectUserViewHolder(@NonNull final LayoutDmUserItemBinding binding,\n                                final OnDirectUserClickListener onClickListener,\n                                final OnDirectUserLongClickListener onLongClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.onClickListener = onClickListener;\n        this.onLongClickListener = onLongClickListener;\n        drawableSize = Utils.convertDpToPx(24);\n    }\n\n    public void bind(final int position,\n                     final User user,\n                     final boolean isAdmin,\n                     final boolean isInviter,\n                     final boolean showSelection,\n                     final boolean isSelected) {\n        if (user == null) return;\n        binding.getRoot().setOnClickListener(v -> {\n            if (onClickListener == null) return;\n            onClickListener.onClick(position, user, isSelected);\n        });\n        binding.getRoot().setOnLongClickListener(v -> {\n            if (onLongClickListener == null) return false;\n            return onLongClickListener.onLongClick(position, user);\n        });\n        setFullName(user);\n        binding.username.setText(user.getUsername());\n        binding.profilePic.setImageURI(user.getProfilePicUrl());\n        setInfo(isAdmin, isInviter);\n        setSelection(showSelection, isSelected);\n    }\n\n    private void setFullName(final User user) {\n        final SpannableStringBuilder sb = new SpannableStringBuilder(user.getFullName());\n        if (user.isVerified()) {\n            if (verifiedSpan == null) {\n                final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);\n                if (verifiedDrawable != null) {\n                    final Drawable drawable = verifiedDrawable.mutate();\n                    drawable.setBounds(0, 0, drawableSize, drawableSize);\n                    verifiedSpan = new VerticalImageSpan(drawable);\n                }\n            }\n            try {\n                if (verifiedSpan != null) {\n                    sb.append(\"  \");\n                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"bind: \", e);\n            }\n        }\n        binding.fullName.setText(sb);\n    }\n\n    private void setInfo(final boolean isAdmin, final boolean isInviter) {\n        if (!isAdmin && !isInviter) {\n            binding.info.setVisibility(View.GONE);\n            return;\n        }\n        if (isAdmin) {\n            binding.info.setText(R.string.admin);\n            return;\n        }\n        binding.info.setText(R.string.inviter);\n    }\n\n    private void setSelection(final boolean showSelection, final boolean isSelected) {\n        binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);\n        binding.getRoot().setSelected(isSelected);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/RecipientThreadViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.directmessages;\n\nimport android.content.res.Resources;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.UserSearchResultsAdapter.OnRecipientClickListener;\nimport awais.instagrabber.databinding.LayoutDmUserItemBinding;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient;\n\npublic class RecipientThreadViewHolder extends RecyclerView.ViewHolder {\n    private static final String TAG = RecipientThreadViewHolder.class.getSimpleName();\n\n    private final LayoutDmUserItemBinding binding;\n    private final OnRecipientClickListener onThreadClickListener;\n    private final float translateAmount;\n\n    public RecipientThreadViewHolder(@NonNull final LayoutDmUserItemBinding binding,\n                                     final OnRecipientClickListener onThreadClickListener) {\n        super(binding.getRoot());\n        this.binding = binding;\n        this.onThreadClickListener = onThreadClickListener;\n        binding.info.setVisibility(View.GONE);\n        final Resources resources = itemView.getResources();\n        final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_inbox_avatar_size);\n        translateAmount = ((float) avatarSize) / 7;\n    }\n\n    public void bind(final int position,\n                     final DirectThread thread,\n                     final boolean showSelection,\n                     final boolean isSelected) {\n        if (thread == null || thread.getUsers().size() == 0) return;\n        binding.getRoot().setOnClickListener(v -> {\n            if (onThreadClickListener == null) return;\n            onThreadClickListener.onClick(position, RankedRecipient.of(thread), isSelected);\n        });\n        binding.fullName.setText(thread.getThreadTitle());\n        setUsername(thread);\n        setProfilePic(thread);\n        setSelection(showSelection, isSelected);\n    }\n\n    private void setProfilePic(final DirectThread thread) {\n        final List<User> users = thread.getUsers();\n        binding.profilePic.setImageURI(users.get(0).getProfilePicUrl());\n        binding.profilePic.setScaleX(1);\n        binding.profilePic.setScaleY(1);\n        binding.profilePic.setTranslationX(0);\n        binding.profilePic.setTranslationY(0);\n        if (users.size() > 1) {\n            binding.profilePic2.setVisibility(View.VISIBLE);\n            binding.profilePic2.setImageURI(users.get(1).getProfilePicUrl());\n            binding.profilePic2.setTranslationX(translateAmount);\n            binding.profilePic2.setTranslationY(translateAmount);\n            final float scaleAmount = 0.75f;\n            binding.profilePic2.setScaleX(scaleAmount);\n            binding.profilePic2.setScaleY(scaleAmount);\n            binding.profilePic.setScaleX(scaleAmount);\n            binding.profilePic.setScaleY(scaleAmount);\n            binding.profilePic.setTranslationX(-translateAmount);\n            binding.profilePic.setTranslationY(-translateAmount);\n            return;\n        }\n        binding.profilePic2.setVisibility(View.GONE);\n    }\n\n    private void setUsername(final DirectThread thread) {\n        if (thread.isGroup()) {\n            binding.username.setVisibility(View.GONE);\n            return;\n        }\n        binding.username.setVisibility(View.VISIBLE);\n        // for a non-group thread, the thread title is the username so set the full name in the username text view\n        binding.username.setText(thread.getUsers().get(0).getFullName());\n    }\n\n    private void setSelection(final boolean showSelection, final boolean isSelected) {\n        binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);\n        binding.getRoot().setSelected(isSelected);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.feed;\n\nimport android.graphics.drawable.Drawable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.transition.TransitionManager;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.RelativeLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.customviews.VerticalImageSpan;\nimport awais.instagrabber.databinding.ItemFeedTopBinding;\nimport awais.instagrabber.databinding.LayoutPostViewBottomBinding;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Caption;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static android.text.TextUtils.TruncateAt.END;\n\npublic abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {\n    public static final int MAX_LINES_COLLAPSED = 5;\n    private final ItemFeedTopBinding topBinding;\n    private final LayoutPostViewBottomBinding bottomBinding;\n    private final ViewGroup bottomFrame;\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback;\n\n    public FeedItemViewHolder(@NonNull final ViewGroup root,\n                              final FeedAdapterV2.FeedItemCallback feedItemCallback) {\n        super(root);\n        this.bottomFrame = root;\n        this.topBinding = ItemFeedTopBinding.bind(root);\n        this.bottomBinding = LayoutPostViewBottomBinding.bind(root);\n        this.feedItemCallback = feedItemCallback;\n    }\n\n    public void bind(final Media media) {\n        if (media == null) {\n            return;\n        }\n        setupProfilePic(media);\n        bottomBinding.date.setText(media.getDate());\n        setupComments(media);\n        setupCaption(media);\n        setupActions(media);\n        if (media.getType() != MediaItemType.MEDIA_TYPE_SLIDER) {\n            bottomBinding.download.setOnClickListener(v ->\n                    feedItemCallback.onDownloadClick(media, -1, bottomBinding.download)\n            );\n        }\n        bindItem(media);\n        bottomFrame.post(() -> setupLocation(media));\n    }\n\n    private void setupComments(@NonNull final Media feedModel) {\n        final long commentsCount = feedModel.getCommentCount();\n        bottomBinding.commentsCount.setText(String.valueOf(commentsCount));\n        bottomBinding.comment.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));\n    }\n\n    private void setupProfilePic(@NonNull final Media media) {\n        final User user = media.getUser();\n        if (user == null) {\n            topBinding.profilePic.setVisibility(View.GONE);\n            topBinding.title.setVisibility(View.GONE);\n            topBinding.subtitle.setVisibility(View.GONE);\n            return;\n        }\n        topBinding.profilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(media));\n        topBinding.profilePic.setImageURI(user.getProfilePicUrl());\n        setupTitle(media);\n    }\n\n    private void setupTitle(@NonNull final Media media) {\n        // final int titleLen = profileModel.getUsername().length() + 1;\n        // final SpannableString spannableString = new SpannableString();\n        // spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);\n        final User user = media.getUser();\n        if (user == null) return;\n        setUsername(user);\n        topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(media));\n        final String fullName = user.getFullName();\n        if (TextUtils.isEmpty(fullName)) {\n            topBinding.subtitle.setVisibility(View.GONE);\n        } else {\n            topBinding.subtitle.setVisibility(View.VISIBLE);\n            topBinding.subtitle.setText(fullName);\n        }\n        topBinding.subtitle.setOnClickListener(v -> feedItemCallback.onNameClick(media));\n    }\n\n    private void setupCaption(final Media media) {\n        bottomBinding.caption.clearOnMentionClickListeners();\n        bottomBinding.caption.clearOnHashtagClickListeners();\n        bottomBinding.caption.clearOnURLClickListeners();\n        bottomBinding.caption.clearOnEmailClickListeners();\n        final Caption caption = media.getCaption();\n        if (caption == null) {\n            bottomBinding.caption.setVisibility(View.GONE);\n            return;\n        }\n        final CharSequence postCaption = caption.getText();\n        final boolean captionEmpty = TextUtils.isEmpty(postCaption);\n        bottomBinding.caption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);\n        if (captionEmpty) return;\n        bottomBinding.caption.setText(postCaption);\n        bottomBinding.caption.setMaxLines(MAX_LINES_COLLAPSED);\n        bottomBinding.caption.setEllipsize(END);\n        bottomBinding.caption.setOnClickListener(v -> bottomFrame.post(() -> {\n            TransitionManager.beginDelayedTransition(bottomFrame);\n            if (bottomBinding.caption.getMaxLines() == MAX_LINES_COLLAPSED) {\n                bottomBinding.caption.setMaxLines(Integer.MAX_VALUE);\n                bottomBinding.caption.setEllipsize(null);\n                return;\n            }\n            bottomBinding.caption.setMaxLines(MAX_LINES_COLLAPSED);\n            bottomBinding.caption.setEllipsize(END);\n        }));\n        bottomBinding.caption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));\n        bottomBinding.caption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));\n        bottomBinding.caption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));\n        bottomBinding.caption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));\n    }\n\n    private void setupLocation(@NonNull final Media media) {\n        final Location location = media.getLocation();\n        if (location == null) {\n            topBinding.location.setVisibility(View.GONE);\n        } else {\n            final String locationName = location.getName();\n            if (TextUtils.isEmpty(locationName)) {\n                topBinding.location.setVisibility(View.GONE);\n            } else {\n                topBinding.location.setVisibility(View.VISIBLE);\n                topBinding.location.setText(locationName);\n                topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(media));\n            }\n        }\n    }\n\n    private void setupActions(@NonNull final Media media) {\n        // temporary - to be set up later\n        bottomBinding.like.setVisibility(View.GONE);\n        bottomBinding.save.setVisibility(View.GONE);\n        bottomBinding.translate.setVisibility(View.GONE);\n        bottomBinding.share.setVisibility(View.GONE);\n    }\n\n    private void setUsername(final User user) {\n        final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());\n        final int drawableSize = Utils.convertDpToPx(24);\n        if (user.isVerified()) {\n            final Drawable verifiedDrawable = itemView.getResources().getDrawable(R.drawable.verified);\n            VerticalImageSpan verifiedSpan = null;\n            if (verifiedDrawable != null) {\n                final Drawable drawable = verifiedDrawable.mutate();\n                drawable.setBounds(0, 0, drawableSize, drawableSize);\n                verifiedSpan = new VerticalImageSpan(drawable);\n            }\n            try {\n                if (verifiedSpan != null) {\n                    sb.append(\"  \");\n                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            } catch (Exception e) {\n                Log.e(\"FeedItemViewHolder\", \"setUsername: \", e);\n            }\n        }\n        topBinding.title.setText(sb);\n    }\n\n    public abstract void bindItem(final Media media);\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.feed;\n\nimport android.net.Uri;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\n\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.databinding.ItemFeedPhotoBinding;\nimport awais.instagrabber.databinding.LayoutPostViewBottomBinding;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class FeedPhotoViewHolder extends FeedItemViewHolder {\n    private static final String TAG = \"FeedPhotoViewHolder\";\n\n    private final ItemFeedPhotoBinding binding;\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback;\n\n    public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,\n                               final FeedAdapterV2.FeedItemCallback feedItemCallback) {\n        super(binding.getRoot(), feedItemCallback);\n        this.binding = binding;\n        this.feedItemCallback = feedItemCallback;\n        final LayoutPostViewBottomBinding bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());\n        bottom.viewsCount.setVisibility(View.GONE);\n        // binding.itemFeedBottom.btnMute.setVisibility(View.GONE);\n        binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);\n        final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())\n                .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)\n                .build();\n        binding.imageViewer.setHierarchy(hierarchy);\n    }\n\n    @Override\n    public void bindItem(final Media media) {\n        if (media == null) return;\n        binding.getRoot().post(() -> {\n            setDimensions(media);\n            final String thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);\n            String url = ResponseBodyUtils.getImageUrl(media);\n            if (TextUtils.isEmpty(url)) url = thumbnailUrl;\n            final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))\n                                                                   // .setLocalThumbnailPreviewsEnabled(true)\n                                                                   // .setProgressiveRenderingEnabled(true)\n                                                                   .build();\n            binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()\n                                                    .setImageRequest(requestBuilder)\n                                                    .setOldController(binding.imageViewer.getController())\n                                                    .setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))\n                                                    .build());\n            binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() {\n                @Override\n                public boolean onSingleTapConfirmed(final MotionEvent e) {\n                    if (feedItemCallback != null) {\n                        feedItemCallback.onPostClick(media);\n                        return true;\n                    }\n                    return false;\n                }\n            });\n        });\n    }\n\n    private void setDimensions(final Media feedModel) {\n        final float aspectRatio = (float) feedModel.getOriginalWidth() / feedModel.getOriginalHeight();\n        binding.imageViewer.setAspectRatio(aspectRatio);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.feed;\n\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\n\nimport androidx.annotation.NonNull;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport java.util.List;\n\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.adapters.SliderCallbackAdapter;\nimport awais.instagrabber.adapters.SliderItemsAdapter;\nimport awais.instagrabber.databinding.ItemFeedSliderBinding;\nimport awais.instagrabber.databinding.LayoutPostViewBottomBinding;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class FeedSliderViewHolder extends FeedItemViewHolder {\n    private static final String TAG = \"FeedSliderViewHolder\";\n    // private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);\n\n    private final ItemFeedSliderBinding binding;\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback;\n    private final LayoutPostViewBottomBinding bottom;\n\n    public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,\n                                final FeedAdapterV2.FeedItemCallback feedItemCallback) {\n        super(binding.getRoot(), feedItemCallback);\n        this.binding = binding;\n        this.feedItemCallback = feedItemCallback;\n        bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());\n        bottom.viewsCount.setVisibility(View.GONE);\n        // bottom.btnMute.setVisibility(View.GONE);\n        final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();\n        layoutParams.height = Utils.displayMetrics.widthPixels + 1;\n        binding.mediaList.setLayoutParams(layoutParams);\n        // final Context context = binding.getRoot().getContext();\n    }\n\n    @Override\n    public void bindItem(final Media feedModel) {\n        final List<Media> sliderItems = feedModel.getCarouselMedia();\n        final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;\n        if (sliderItemLen <= 0) return;\n        final String text = \"1/\" + sliderItemLen;\n        binding.mediaCounter.setText(text);\n        binding.mediaList.setOffscreenPageLimit(1);\n        final SliderItemsAdapter adapter = new SliderItemsAdapter(false, new SliderCallbackAdapter() {\n            @Override\n            public void onItemClicked(final int position, final Media media, final View view) {\n                feedItemCallback.onSliderClick(feedModel, position);\n            }\n        });\n        binding.mediaList.setAdapter(adapter);\n        binding.mediaList.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {\n            @Override\n            public void onPageSelected(final int position) {\n                if (position >= sliderItemLen) return;\n                final String text = (position + 1) + \"/\" + sliderItemLen;\n                binding.mediaCounter.setText(text);\n                setDimensions(binding.mediaList, sliderItems.get(position));\n                bottom.download.setOnClickListener(v ->\n                        feedItemCallback.onDownloadClick(feedModel, position, bottom.download)\n                );\n            }\n        });\n        setDimensions(binding.mediaList, sliderItems.get(0));\n        bottom.download.setOnClickListener(v ->\n                feedItemCallback.onDownloadClick(feedModel, 0, bottom.download)\n        );\n        adapter.submitList(sliderItems);\n    }\n\n    private void setDimensions(final View view, final Media model) {\n        final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();\n        int requiredWidth = layoutParams.width;\n        if (requiredWidth <= 0) {\n            final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {\n                @Override\n                public boolean onPreDraw() {\n                    view.getViewTreeObserver().removeOnPreDrawListener(this);\n                    setLayoutParamDimens(binding.mediaList, model);\n                    return true;\n                }\n            };\n            view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);\n            return;\n        }\n        setLayoutParamDimens(binding.mediaList, model);\n    }\n\n    private void setLayoutParamDimens(final View view, final Media model) {\n        final int requiredWidth = view.getMeasuredWidth();\n        final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();\n        final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getOriginalHeight(), model.getOriginalWidth());\n        layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight;\n        view.requestLayout();\n    }\n\n    // private void autoPlay(final int position) {\n    // if (!shouldAutoPlay) {\n    //     return;\n    // }\n    // final ChildMediaItemsAdapter adapter = (ChildMediaItemsAdapter) binding.mediaList.getAdapter();\n    // if (adapter == null) {\n    //     return;\n    // }\n    // final ViewerPostModel sliderItem = adapter.getItemAtPosition(position);\n    // if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {\n    //     return;\n    // }\n    // final ViewSwitcher viewSwitcher = (ViewSwitcher) binding.mediaList.getChildAt(position);\n    // loadPlayer(binding.getRoot().getContext(),\n    //            position, sliderItem.getDisplayUrl(),\n    //            viewSwitcher,\n    //            cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory,\n    //            playerChangeListener);\n    // }\n\n    // public void startPlayingVideo() {\n    //     final int playerPosition = 0;\n    //     autoPlay(playerPosition);\n    // }\n    //\n    // public void stopPlayingVideo() {\n    //     if (pagerPlayer == null) {\n    //         return;\n    //     }\n    //     pagerPlayer.setPlayWhenReady(false);\n    // }\n\n    // private interface PlayerChangeListener {\n    //     void playerChanged(final int position, final SimpleExoPlayer player);\n    // }\n    //\n    // private static void loadPlayer(final Context context,\n    //                                final int position,\n    //                                final String displayUrl,\n    //                                final ViewSwitcher viewSwitcher,\n    //                                final DataSource.Factory factory,\n    //                                final PlayerChangeListener playerChangeListener) {\n    //     if (viewSwitcher == null) {\n    //         return;\n    //     }\n    //     SimpleExoPlayer player = (SimpleExoPlayer) viewSwitcher.getTag();\n    //     if (player != null) {\n    //         player.setPlayWhenReady(true);\n    //         return;\n    //     }\n    //     player = new SimpleExoPlayer.Builder(context).build();\n    //     final PlayerView playerView = (PlayerView) viewSwitcher.getChildAt(1);\n    //     playerView.setPlayer(player);\n    //     if (viewSwitcher.getDisplayedChild() == 0) {\n    //         viewSwitcher.showNext();\n    //     }\n    //     playerView.setControllerShowTimeoutMs(1000);\n    //     float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;\n    //     if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;\n    //     player.setVolume(vol);\n    //     player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));\n    //     final MediaItem mediaItem = MediaItem.fromUri(displayUrl);\n    //     final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem);\n    //     player.setRepeatMode(Player.REPEAT_MODE_ALL);\n    //     player.setMediaSource(mediaSource);\n    //     player.prepare();\n    //     player.setVolume(vol);\n    //     playerChangeListener.playerChanged(position, player);\n    //     viewSwitcher.setTag(player);\n    // }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java",
    "content": "package awais.instagrabber.adapters.viewholder.feed;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.constraintlayout.widget.ConstraintLayout;\n\nimport com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;\nimport com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;\nimport com.google.android.exoplayer2.upstream.cache.SimpleCache;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.customviews.VideoPlayerCallbackAdapter;\nimport awais.instagrabber.customviews.VideoPlayerViewHelper;\nimport awais.instagrabber.databinding.ItemFeedVideoBinding;\nimport awais.instagrabber.databinding.LayoutPostViewBottomBinding;\nimport awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.MediaCandidate;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class FeedVideoViewHolder extends FeedItemViewHolder {\n    private static final String TAG = \"FeedVideoViewHolder\";\n\n    private final ItemFeedVideoBinding binding;\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback;\n    private final Handler handler;\n    private final DefaultDataSourceFactory dataSourceFactory;\n\n    private final LayoutPostViewBottomBinding bottom;\n    private CacheDataSourceFactory cacheDataSourceFactory;\n    private Media media;\n\n    // private final Runnable loadRunnable = new Runnable() {\n    //     @Override\n    //     public void run() {\n    //         // loadPlayer(feedModel);\n    //     }\n    // };\n\n    public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,\n                               final FeedAdapterV2.FeedItemCallback feedItemCallback) {\n        super(binding.getRoot(), feedItemCallback);\n        bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());\n        this.binding = binding;\n        this.feedItemCallback = feedItemCallback;\n        bottom.viewsCount.setVisibility(View.VISIBLE);\n        handler = new Handler(Looper.getMainLooper());\n        final Context context = binding.getRoot().getContext();\n        dataSourceFactory = new DefaultDataSourceFactory(context, \"instagram\");\n        final SimpleCache simpleCache = Utils.getSimpleCacheInstance(context);\n        if (simpleCache != null) {\n            cacheDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory);\n        }\n    }\n\n    @Override\n    public void bindItem(final Media media) {\n        // Log.d(TAG, \"Binding post: \" + feedModel.getPostId());\n        this.media = media;\n        final String viewCount = itemView.getResources().getQuantityString(R.plurals.views_count, (int) media.getViewCount(), media.getViewCount());\n        bottom.viewsCount.setText(viewCount);\n        final LayoutVideoPlayerWithThumbnailBinding videoPost =\n                LayoutVideoPlayerWithThumbnailBinding.inflate(LayoutInflater.from(itemView.getContext()), binding.getRoot(), false);\n        final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) videoPost.getRoot().getLayoutParams();\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(media.getOriginalHeight(),\n                media.getOriginalWidth(),\n                (int) (Utils.displayMetrics.heightPixels * 0.8),\n                Utils.displayMetrics.widthPixels);\n        layoutParams.width = ConstraintLayout.LayoutParams.MATCH_PARENT;\n        layoutParams.height = widthHeight.second;\n        final View postView = videoPost.getRoot();\n        binding.postContainer.addView(postView);\n        final float vol = settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;\n        final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {\n\n            @Override\n            public void onThumbnailClick() {\n                feedItemCallback.onPostClick(media);\n            }\n\n            @Override\n            public void onPlayerViewLoaded() {\n                final ViewGroup.LayoutParams layoutParams = videoPost.playerView.getLayoutParams();\n                final int requiredWidth = Utils.displayMetrics.widthPixels;\n                final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());\n                layoutParams.width = requiredWidth;\n                layoutParams.height = resultingHeight;\n                videoPost.playerView.requestLayout();\n            }\n        };\n        final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();\n        String videoUrl = null;\n        final List<MediaCandidate> videoVersions = media.getVideoVersions();\n        if (videoVersions != null && !videoVersions.isEmpty()) {\n            final MediaCandidate videoVersion = videoVersions.get(0);\n            videoUrl = videoVersion.getUrl();\n        }\n        final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),\n                                                                                      videoPost,\n                                                                                      videoUrl,\n                                                                                      vol,\n                                                                                      aspectRatio,\n                                                                                      ResponseBodyUtils.getThumbUrl(media),\n                                                                                      false,\n                                                                                      // null,\n                                                                                      videoPlayerCallback);\n        videoPost.thumbnail.post(() -> {\n            if (media.getOriginalHeight() > 0.8 * Utils.displayMetrics.heightPixels) {\n                final ViewGroup.LayoutParams tLayoutParams = videoPost.thumbnail.getLayoutParams();\n                tLayoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);\n                videoPost.thumbnail.requestLayout();\n            }\n        });\n    }\n\n    public Media getCurrentFeedModel() {\n        return media;\n    }\n\n    // public void stopPlaying() {\n    //     // Log.d(TAG, \"Stopping post: \" + feedModel.getPostId() + \", player: \" + player + \", player.isPlaying: \" + (player != null && player.isPlaying()));\n    //     handler.removeCallbacks(loadRunnable);\n    //     if (player != null) {\n    //         player.release();\n    //     }\n    //     if (videoPost.root.getDisplayedChild() == 1) {\n    //         videoPost.root.showPrevious();\n    //     }\n    // }\n    //\n    // public void startPlaying() {\n    //     handler.removeCallbacks(loadRunnable);\n    //     handler.postDelayed(loadRunnable, 800);\n    // }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/CubicBezierInterpolator.java",
    "content": "package awais.instagrabber.animations;\n\nimport android.graphics.PointF;\nimport android.view.animation.Interpolator;\n\npublic class CubicBezierInterpolator implements Interpolator {\n\n    public static final CubicBezierInterpolator DEFAULT = new CubicBezierInterpolator(0.25, 0.1, 0.25, 1);\n    public static final CubicBezierInterpolator EASE_OUT = new CubicBezierInterpolator(0, 0, .58, 1);\n    public static final CubicBezierInterpolator EASE_OUT_QUINT = new CubicBezierInterpolator(.23, 1, .32, 1);\n    public static final CubicBezierInterpolator EASE_IN = new CubicBezierInterpolator(.42, 0, 1, 1);\n    public static final CubicBezierInterpolator EASE_BOTH = new CubicBezierInterpolator(.42, 0, .58, 1);\n\n    protected PointF start;\n    protected PointF end;\n    protected PointF a = new PointF();\n    protected PointF b = new PointF();\n    protected PointF c = new PointF();\n\n    public CubicBezierInterpolator(PointF start, PointF end) throws IllegalArgumentException {\n        if (start.x < 0 || start.x > 1) {\n            throw new IllegalArgumentException(\"startX value must be in the range [0, 1]\");\n        }\n        if (end.x < 0 || end.x > 1) {\n            throw new IllegalArgumentException(\"endX value must be in the range [0, 1]\");\n        }\n        this.start = start;\n        this.end = end;\n    }\n\n    public CubicBezierInterpolator(float startX, float startY, float endX, float endY) {\n        this(new PointF(startX, startY), new PointF(endX, endY));\n    }\n\n    public CubicBezierInterpolator(double startX, double startY, double endX, double endY) {\n        this((float) startX, (float) startY, (float) endX, (float) endY);\n    }\n\n    @Override\n    public float getInterpolation(float time) {\n        return getBezierCoordinateY(getXForTime(time));\n    }\n\n    protected float getBezierCoordinateY(float time) {\n        c.y = 3 * start.y;\n        b.y = 3 * (end.y - start.y) - c.y;\n        a.y = 1 - c.y - b.y;\n        return time * (c.y + time * (b.y + time * a.y));\n    }\n\n    protected float getXForTime(float time) {\n        float x = time;\n        float z;\n        for (int i = 1; i < 14; i++) {\n            z = getBezierCoordinateX(x) - time;\n            if (Math.abs(z) < 1e-3) {\n                break;\n            }\n            x -= z / getXDerivate(x);\n        }\n        return x;\n    }\n\n    private float getXDerivate(float t) {\n        return c.x + t * (2 * b.x + 3 * a.x * t);\n    }\n\n    private float getBezierCoordinateX(float time) {\n        c.x = 3 * start.x;\n        b.x = 3 * (end.x - start.x) - c.x;\n        a.x = 1 - c.x - b.x;\n        return time * (c.x + time * (b.x + time * a.x));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/FabAnimation.java",
    "content": "package awais.instagrabber.animations;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.view.View;\n\n// https://medium.com/better-programming/animated-fab-button-with-more-options-2dcf7118fff6\n\npublic class FabAnimation {\n    public static boolean rotateFab(final View v, boolean rotate) {\n        v.animate().setDuration(200)\n                .setListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        super.onAnimationEnd(animation);\n                    }\n                })\n                .rotation(rotate ? 135f : 0f);\n        return rotate;\n    }\n\n    public static void showIn(final View v) {\n        v.setVisibility(View.VISIBLE);\n        v.setAlpha(0f);\n        v.setTranslationY(v.getHeight());\n        v.animate()\n                .setDuration(200)\n                .translationY(0)\n                .setListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        super.onAnimationEnd(animation);\n                    }\n                })\n                .alpha(1f)\n                .start();\n    }\n\n    public static void showOut(final View v) {\n        v.setVisibility(View.VISIBLE);\n        v.setAlpha(1f);\n        v.setTranslationY(0);\n        v.animate()\n                .setDuration(200)\n                .translationY(v.getHeight())\n                .setListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        v.setVisibility(View.GONE);\n                        super.onAnimationEnd(animation);\n                    }\n                }).alpha(0f)\n                .start();\n    }\n\n    public static void init(final View v) {\n        v.setVisibility(View.GONE);\n        v.setTranslationY(v.getHeight());\n        v.setAlpha(0f);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/ResizeAnimation.java",
    "content": "package awais.instagrabber.animations;\n\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.view.animation.Transformation;\n\npublic class ResizeAnimation extends Animation {\n    private static final String TAG = \"ResizeAnimation\";\n\n    final View view;\n    final int startHeight;\n    final int targetHeight;\n    final int startWidth;\n    final int targetWidth;\n\n    public ResizeAnimation(final View view,\n                           final int startHeight,\n                           final int startWidth,\n                           final int targetHeight,\n                           final int targetWidth) {\n        this.view = view;\n        this.startHeight = startHeight;\n        this.targetHeight = targetHeight;\n        this.startWidth = startWidth;\n        this.targetWidth = targetWidth;\n    }\n\n    @Override\n    protected void applyTransformation(final float interpolatedTime, final Transformation t) {\n        // Log.d(TAG, \"applyTransformation: interpolatedTime: \" + interpolatedTime);\n        view.getLayoutParams().height = (int) (startHeight + (targetHeight - startHeight) * interpolatedTime);\n        view.getLayoutParams().width = (int) (startWidth + (targetWidth - startWidth) * interpolatedTime);\n        view.requestLayout();\n    }\n\n    @Override\n    public void initialize(final int width, final int height, final int parentWidth, final int parentHeight) {\n        super.initialize(width, height, parentWidth, parentHeight);\n    }\n\n    @Override\n    public boolean willChangeBounds() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/RevealOutlineAnimation.java",
    "content": "package awais.instagrabber.animations;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.graphics.Outline;\nimport android.graphics.Rect;\nimport android.view.View;\nimport android.view.ViewOutlineProvider;\n\n/**\n * A {@link ViewOutlineProvider} that has helper functions to create reveal animations.\n * This class should be extended so that subclasses can define the reveal shape as the\n * animation progresses from 0 to 1.\n */\npublic abstract class RevealOutlineAnimation extends ViewOutlineProvider {\n    protected Rect mOutline;\n    protected float mOutlineRadius;\n\n    public RevealOutlineAnimation() {\n        mOutline = new Rect();\n    }\n\n    /**\n     * Returns whether elevation should be removed for the duration of the reveal animation.\n     */\n    abstract boolean shouldRemoveElevationDuringAnimation();\n\n    /**\n     * Sets the progress, from 0 to 1, of the reveal animation.\n     */\n    abstract void setProgress(float progress);\n\n    public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {\n        ValueAnimator va =\n                isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);\n        final float elevation = revealView.getElevation();\n\n        va.addListener(new AnimatorListenerAdapter() {\n            private boolean mIsClippedToOutline;\n            private ViewOutlineProvider mOldOutlineProvider;\n\n            public void onAnimationStart(Animator animation) {\n                mIsClippedToOutline = revealView.getClipToOutline();\n                mOldOutlineProvider = revealView.getOutlineProvider();\n\n                revealView.setOutlineProvider(RevealOutlineAnimation.this);\n                revealView.setClipToOutline(true);\n                if (shouldRemoveElevationDuringAnimation()) {\n                    revealView.setTranslationZ(-elevation);\n                }\n            }\n\n            public void onAnimationEnd(Animator animation) {\n                revealView.setOutlineProvider(mOldOutlineProvider);\n                revealView.setClipToOutline(mIsClippedToOutline);\n                if (shouldRemoveElevationDuringAnimation()) {\n                    revealView.setTranslationZ(0);\n                }\n            }\n\n        });\n\n        va.addUpdateListener(v -> {\n            float progress = (Float) v.getAnimatedValue();\n            setProgress(progress);\n            revealView.invalidateOutline();\n        });\n        return va;\n    }\n\n    @Override\n    public void getOutline(View v, Outline outline) {\n        outline.setRoundRect(mOutline, mOutlineRadius);\n    }\n\n    public float getRadius() {\n        return mOutlineRadius;\n    }\n\n    public void getOutline(Rect out) {\n        out.set(mOutline);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/RoundedRectRevealOutlineProvider.java",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage awais.instagrabber.animations;\n\nimport android.graphics.Rect;\n\n/**\n * A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii\n * and two {@link Rect}s.\n * <p>\n * An example usage of this provider is an outline that starts out as a circle and ends\n * as a rounded rectangle.\n */\npublic class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {\n    private final float mStartRadius;\n    private final float mEndRadius;\n\n    private final Rect mStartRect;\n    private final Rect mEndRect;\n\n    public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect, Rect endRect) {\n        mStartRadius = startRadius;\n        mEndRadius = endRadius;\n        mStartRect = startRect;\n        mEndRect = endRect;\n    }\n\n    @Override\n    public boolean shouldRemoveElevationDuringAnimation() {\n        return false;\n    }\n\n    @Override\n    public void setProgress(float progress) {\n        mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;\n\n        mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);\n        mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);\n        mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);\n        mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/animations/ScaleAnimation.java",
    "content": "package awais.instagrabber.animations;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\n\npublic class ScaleAnimation {\n\n    private final View view;\n\n    public ScaleAnimation(View view) {\n        this.view = view;\n    }\n\n\n    public void start() {\n        AnimatorSet set = new AnimatorSet();\n        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, \"scaleY\", 2.0f);\n\n        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, \"scaleX\", 2.0f);\n        set.setDuration(150);\n        set.setInterpolator(new AccelerateDecelerateInterpolator());\n        set.playTogether(scaleY, scaleX);\n        set.start();\n    }\n\n    public void stop() {\n        AnimatorSet set = new AnimatorSet();\n        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, \"scaleY\", 1.0f);\n        //        scaleY.setDuration(250);\n        //        scaleY.setInterpolator(new DecelerateInterpolator());\n\n\n        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, \"scaleX\", 1.0f);\n        //        scaleX.setDuration(250);\n        //        scaleX.setInterpolator(new DecelerateInterpolator());\n\n\n        set.setDuration(150);\n        set.setInterpolator(new AccelerateDecelerateInterpolator());\n        set.playTogether(scaleY, scaleX);\n        set.start();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/DiscoverPostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;\nimport awais.instagrabber.repositories.responses.WrappedMedia;\nimport awais.instagrabber.webservices.DiscoverService;\nimport awais.instagrabber.webservices.ServiceCallback;\n\npublic class DiscoverPostFetchService implements PostFetcher.PostFetchService {\n    private static final String TAG = \"DiscoverPostFetchService\";\n    private final DiscoverService discoverService;\n    private final DiscoverService.TopicalExploreRequest topicalExploreRequest;\n    private boolean moreAvailable = false;\n\n    public DiscoverPostFetchService(final DiscoverService.TopicalExploreRequest topicalExploreRequest) {\n        this.topicalExploreRequest = topicalExploreRequest;\n        discoverService = DiscoverService.getInstance();\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<TopicalExploreFeedResponse>() {\n            @Override\n            public void onSuccess(final TopicalExploreFeedResponse result) {\n                if (result == null) {\n                    onFailure(new RuntimeException(\"result is null\"));\n                    return;\n                }\n                moreAvailable = result.getMoreAvailable();\n                topicalExploreRequest.setMaxId(result.getNextMaxId());\n                final List<WrappedMedia> items = result.getItems();\n                final List<Media> posts;\n                if (items == null) {\n                    posts = Collections.emptyList();\n                } else {\n                    posts = items.stream()\n                                 .map(WrappedMedia::getMedia)\n                                 .filter(Objects::nonNull)\n                                 .collect(Collectors.toList());\n                }\n                if (fetchListener != null) {\n                    fetchListener.onResult(posts);\n                }\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void reset() {\n        topicalExploreRequest.setMaxId(null);\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return moreAvailable;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.webservices.FeedService;\nimport awais.instagrabber.webservices.ServiceCallback;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class FeedPostFetchService implements PostFetcher.PostFetchService {\n    private static final String TAG = \"FeedPostFetchService\";\n    private final FeedService feedService;\n    private String nextCursor;\n    private boolean hasNextPage;\n\n    public FeedPostFetchService() {\n        feedService = FeedService.getInstance();\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        final List<Media> feedModels = new ArrayList<>();\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);\n        feedModels.clear();\n        feedService.fetch(csrfToken, deviceUuid, nextCursor, new ServiceCallback<PostsFetchResponse>() {\n            @Override\n            public void onSuccess(final PostsFetchResponse result) {\n                if (result == null && feedModels.size() > 0) {\n                    fetchListener.onResult(feedModels);\n                    return;\n                } else if (result == null) return;\n                nextCursor = result.getNextCursor();\n                hasNextPage = result.getHasNextPage();\n\n                final List<Media> mediaResults = result.getFeedModels();\n                feedModels.addAll(mediaResults);\n\n                if (fetchListener != null) {\n                    // if (feedModels.size() < 15 && hasNextPage) {\n                    //     feedService.fetch(csrfToken, nextCursor, this);\n                    // } else {\n                    fetchListener.onResult(feedModels);\n                    // }\n                }\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void reset() {\n        nextCursor = null;\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return hasNextPage;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport awais.instagrabber.webservices.TagsService;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class HashtagPostFetchService implements PostFetcher.PostFetchService {\n    private final TagsService tagsService;\n    private final GraphQLRepository graphQLRepository;\n    private final Hashtag hashtagModel;\n    private String nextMaxId;\n    private boolean moreAvailable;\n    private final boolean isLoggedIn;\n\n    public HashtagPostFetchService(final Hashtag hashtagModel, final boolean isLoggedIn) {\n        this.hashtagModel = hashtagModel;\n        this.isLoggedIn = isLoggedIn;\n        tagsService = isLoggedIn ? TagsService.getInstance() : null;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        final ServiceCallback<PostsFetchResponse> cb = new ServiceCallback<PostsFetchResponse>() {\n            @Override\n            public void onSuccess(final PostsFetchResponse result) {\n                if (result == null) return;\n                nextMaxId = result.getNextCursor();\n                moreAvailable = result.getHasNextPage();\n                if (fetchListener != null) {\n                    fetchListener.onResult(result.getFeedModels());\n                }\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                // Log.e(TAG, \"onFailure: \", t);\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n            }\n        };\n        if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);\n        else graphQLRepository.fetchHashtagPosts(\n                hashtagModel.getName().toLowerCase(),\n                nextMaxId,\n                CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> {\n                    if (throwable != null) {\n                        cb.onFailure(throwable);\n                        return;\n                    }\n                    cb.onSuccess(postsFetchResponse);\n                }, Dispatchers.getIO())\n        );\n    }\n\n    @Override\n    public void reset() {\n        nextMaxId = null;\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return moreAvailable;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.LocationService;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class LocationPostFetchService implements PostFetcher.PostFetchService {\n    private final LocationService locationService;\n    private final GraphQLRepository graphQLRepository;\n    private final Location locationModel;\n    private String nextMaxId;\n    private boolean moreAvailable;\n    private final boolean isLoggedIn;\n\n    public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) {\n        this.locationModel = locationModel;\n        this.isLoggedIn = isLoggedIn;\n        locationService = isLoggedIn ? LocationService.getInstance() : null;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        final ServiceCallback<PostsFetchResponse> cb = new ServiceCallback<PostsFetchResponse>() {\n            @Override\n            public void onSuccess(final PostsFetchResponse result) {\n                if (result == null) return;\n                nextMaxId = result.getNextCursor();\n                moreAvailable = result.getHasNextPage();\n                if (fetchListener != null) {\n                    fetchListener.onResult(result.getFeedModels());\n                }\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                // Log.e(TAG, \"onFailure: \", t);\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n            }\n        };\n        if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb);\n        else graphQLRepository.fetchLocationPosts(\n                locationModel.getPk(),\n                nextMaxId,\n                CoroutineUtilsKt.getContinuation((postsFetchResponse, throwable) -> {\n                    if (throwable != null) {\n                        cb.onFailure(throwable);\n                        return;\n                    }\n                    cb.onSuccess(postsFetchResponse);\n                }, Dispatchers.getIO())\n        );\n    }\n\n    @Override\n    public void reset() {\n        nextMaxId = null;\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return moreAvailable;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.ProfileRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlin.coroutines.Continuation;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class ProfilePostFetchService implements PostFetcher.PostFetchService {\n    private static final String TAG = \"ProfilePostFetchService\";\n    private final ProfileRepository profileRepository;\n    private final GraphQLRepository graphQLRepository;\n    private final User profileModel;\n    private final boolean isLoggedIn;\n    private String nextMaxId;\n    private boolean moreAvailable;\n\n    public ProfilePostFetchService(final User profileModel, final boolean isLoggedIn) {\n        this.profileModel = profileModel;\n        this.isLoggedIn = isLoggedIn;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n        profileRepository = isLoggedIn ? ProfileRepository.Companion.getInstance() : null;\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        final Continuation<PostsFetchResponse> cb = CoroutineUtilsKt.getContinuation((result, t) -> {\n            if (t != null) {\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n                return;\n            }\n            if (result == null) return;\n            nextMaxId = result.getNextCursor();\n            moreAvailable = result.getHasNextPage();\n            if (fetchListener != null) {\n                fetchListener.onResult(result.getFeedModels());\n            }\n        }, Dispatchers.getIO());\n        if (isLoggedIn) profileRepository.fetchPosts(profileModel.getPk(), nextMaxId, cb);\n        else graphQLRepository.fetchProfilePosts(\n                profileModel.getPk(),\n                30,\n                nextMaxId,\n                profileModel,\n                cb\n        );\n    }\n\n    @Override\n    public void reset() {\n        nextMaxId = null;\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return moreAvailable;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java",
    "content": "package awais.instagrabber.asyncs;\n\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.models.enums.PostItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.ProfileRepository;\nimport kotlin.coroutines.Continuation;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class SavedPostFetchService implements PostFetcher.PostFetchService {\n    private final ProfileRepository profileRepository;\n    private final GraphQLRepository graphQLRepository;\n    private final long profileId;\n    private final PostItemType type;\n    private final boolean isLoggedIn;\n\n    private String nextMaxId;\n    private final String collectionId;\n    private boolean moreAvailable;\n\n    public SavedPostFetchService(final long profileId, final PostItemType type, final boolean isLoggedIn, final String collectionId) {\n        this.profileId = profileId;\n        this.type = type;\n        this.isLoggedIn = isLoggedIn;\n        this.collectionId = collectionId;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n        profileRepository = isLoggedIn ? ProfileRepository.Companion.getInstance() : null;\n    }\n\n    @Override\n    public void fetch(final FetchListener<List<Media>> fetchListener) {\n        final Continuation<PostsFetchResponse> callback = CoroutineUtilsKt.getContinuation((result, t) -> {\n            if (t != null) {\n                if (fetchListener != null) {\n                    fetchListener.onFailure(t);\n                }\n                return;\n            }\n            if (result == null) return;\n            nextMaxId = result.getNextCursor();\n            moreAvailable = result.getHasNextPage();\n            if (fetchListener != null) {\n                fetchListener.onResult(result.getFeedModels());\n            }\n        }, Dispatchers.getIO());\n        switch (type) {\n            case LIKED:\n                profileRepository.fetchLiked(nextMaxId, callback);\n                break;\n            case TAGGED:\n                if (isLoggedIn) profileRepository.fetchTagged(profileId, nextMaxId, callback);\n                else graphQLRepository.fetchTaggedPosts(\n                        profileId,\n                        30,\n                        nextMaxId,\n                        callback\n                );\n                break;\n            case COLLECTION:\n            case SAVED:\n                profileRepository.fetchSaved(nextMaxId, collectionId, callback);\n                break;\n        }\n    }\n\n    @Override\n    public void reset() {\n        nextMaxId = null;\n    }\n\n    @Override\n    public boolean hasNextPage() {\n        return moreAvailable;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/backup/BarinstaBackupAgent.kt",
    "content": "package awais.instagrabber.backup\n\nimport android.app.backup.BackupAgent\nimport android.app.backup.BackupDataInput\nimport android.app.backup.BackupDataOutput\nimport android.app.backup.FullBackupDataOutput\nimport android.os.ParcelFileDescriptor\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.utils.Utils.settingsHelper\n\nclass BarinstaBackupAgent : BackupAgent() {\n    override fun onFullBackup(data: FullBackupDataOutput?) {\n        super.onFullBackup(if (settingsHelper.getBoolean(PreferenceKeys.PREF_AUTO_BACKUP_ENABLED)) data else null)\n    }\n\n    // no key-value backups\n    override fun onBackup(oldState: ParcelFileDescriptor?,\n                          data: BackupDataOutput?, newState: ParcelFileDescriptor?) {}\n\n    override fun onRestore(data: BackupDataInput, appVersionCode: Int,\n                           newState: ParcelFileDescriptor) {}\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/broadcasts/DMRefreshBroadcastReceiver.java",
    "content": "package awais.instagrabber.broadcasts;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\n\npublic class DMRefreshBroadcastReceiver extends BroadcastReceiver {\n    public static final String ACTION_REFRESH_DM = \"action_refresh_dm\";\n    private final OnDMRefreshCallback callback;\n\n    public DMRefreshBroadcastReceiver(final OnDMRefreshCallback callback) {\n        this.callback = callback;\n    }\n\n    @Override\n    public void onReceive(final Context context, final Intent intent) {\n        if (callback == null) return;\n        final String action = intent.getAction();\n        if (action == null) return;\n        if (!action.equals(ACTION_REFRESH_DM)) return;\n        callback.onReceive();\n    }\n\n    public interface OnDMRefreshCallback {\n        void onReceive();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/BarinstaFragmentNavigator.kt",
    "content": "package awais.instagrabber.customviews\n\nimport android.content.Context\nimport androidx.fragment.app.FragmentManager\nimport androidx.navigation.NavBackStackEntry\nimport androidx.navigation.NavOptions\nimport androidx.navigation.Navigator\nimport androidx.navigation.fragment.FragmentNavigator\nimport androidx.navigation.navOptions\nimport awais.instagrabber.R\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.utils.Utils\n\nprivate val defaultNavOptions = navOptions {\n    anim {\n        enter = R.anim.slide_in_right\n        exit = R.anim.slide_out_left\n        popEnter = android.R.anim.slide_in_left\n        popExit = android.R.anim.slide_out_right\n    }\n}\n\nprivate val emptyNavOptions = navOptions {}\n\n/**\n * Needs to replace FragmentNavigator and replacing is done with name in annotation.\n * Navigation method will use defaults for fragments transitions animations.\n */\n@Navigator.Name(\"fragment\")\nclass BarinstaFragmentNavigator(\n    context: Context,\n    fragmentManager: FragmentManager,\n    containerId: Int\n) : FragmentNavigator(context, fragmentManager, containerId) {\n\n    override fun navigate(\n        entries: List<NavBackStackEntry>,\n        navOptions: NavOptions?,\n        navigatorExtras: Navigator.Extras?\n    ) {\n        val disableTransitions = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS)\n        if (disableTransitions) {\n            super.navigate(entries, navOptions, navigatorExtras)\n            return\n        }\n        // this will try to fill in empty animations with defaults when no shared element transitions\n        // https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element\n        val hasSharedElements = navigatorExtras != null && navigatorExtras is Extras\n        val navOptions1 = if (hasSharedElements) navOptions else navOptions.fillEmptyAnimationsWithDefaults()\n        super.navigate(entries, navOptions1, navigatorExtras)\n    }\n\n    private fun NavOptions?.fillEmptyAnimationsWithDefaults(): NavOptions =\n        this?.copyNavOptionsWithDefaultAnimations() ?: defaultNavOptions\n\n    private fun NavOptions.copyNavOptionsWithDefaultAnimations(): NavOptions = let { originalNavOptions ->\n        navOptions {\n            launchSingleTop = originalNavOptions.shouldLaunchSingleTop()\n            popUpTo(originalNavOptions.popUpToId) {\n                inclusive = originalNavOptions.isPopUpToInclusive()\n                saveState = originalNavOptions.shouldPopUpToSaveState()\n            }\n            originalNavOptions.popUpToRoute?.let {\n                popUpTo(it) {\n                    inclusive = originalNavOptions.isPopUpToInclusive()\n                    saveState = originalNavOptions.shouldPopUpToSaveState()\n                }\n            }\n            restoreState = originalNavOptions.shouldRestoreState()\n            anim {\n                enter =\n                    if (originalNavOptions.enterAnim == emptyNavOptions.enterAnim) defaultNavOptions.enterAnim\n                    else originalNavOptions.enterAnim\n                exit =\n                    if (originalNavOptions.exitAnim == emptyNavOptions.exitAnim) defaultNavOptions.exitAnim\n                    else originalNavOptions.exitAnim\n                popEnter =\n                    if (originalNavOptions.popEnterAnim == emptyNavOptions.popEnterAnim) defaultNavOptions.popEnterAnim\n                    else originalNavOptions.popEnterAnim\n                popExit =\n                    if (originalNavOptions.popExitAnim == emptyNavOptions.popExitAnim) defaultNavOptions.popExitAnim\n                    else originalNavOptions.popExitAnim\n            }\n        }\n    }\n\n    private companion object {\n        private const val TAG = \"FragmentNavigator\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/BarinstaNavHostFragment.kt",
    "content": "package awais.instagrabber.customviews\n\nimport androidx.navigation.NavHostController\nimport androidx.navigation.fragment.NavHostFragment\n\nclass BarinstaNavHostFragment : NavHostFragment() {\n    override fun onCreateNavHostController(navHostController: NavHostController) {\n        super.onCreateNavHostController(navHostController)\n        navHostController.navigatorProvider.addNavigator(\n            // this replaces FragmentNavigator\n            BarinstaFragmentNavigator(requireContext(), childFragmentManager, id)\n        )\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport awais.instagrabber.R;\n\npublic class ChatMessageLayout extends FrameLayout {\n\n    private FrameLayout viewPartMain;\n    private View viewPartInfo;\n    private TypedArray a;\n\n    private int viewPartInfoWidth;\n    private int viewPartInfoHeight;\n\n    // private boolean withGroupHeader = false;\n\n    public ChatMessageLayout(@NonNull final Context context) {\n        super(context);\n    }\n\n    public ChatMessageLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        a = context.obtainStyledAttributes(attrs, R.styleable.ChatMessageLayout, 0, 0);\n    }\n\n    public ChatMessageLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        a = context.obtainStyledAttributes(attrs, R.styleable.ChatMessageLayout, defStyleAttr, 0);\n    }\n\n    public ChatMessageLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n        a = context.obtainStyledAttributes(attrs, R.styleable.ChatMessageLayout, defStyleAttr, defStyleRes);\n    }\n\n    // public void setWithGroupHeader(boolean withGroupHeader) {\n    //     this.withGroupHeader = withGroupHeader;\n    // }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        try {\n            viewPartMain = findViewById(a.getResourceId(R.styleable.ChatMessageLayout_viewPartMain, -1));\n            viewPartInfo = findViewById(a.getResourceId(R.styleable.ChatMessageLayout_viewPartInfo, -1));\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightSize;\n        // heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\n        if (viewPartMain == null || viewPartInfo == null || widthSize <= 0) {\n            return;\n        }\n\n        final View firstChild = viewPartMain.getChildAt(0);\n        if (firstChild == null) return;\n\n        final int firstChildId = firstChild.getId();\n\n        int availableWidth = widthSize - getPaddingLeft() - getPaddingRight();\n        // int availableHeight = heightSize - getPaddingTop() - getPaddingBottom();\n\n        final LayoutParams viewPartMainLayoutParams = (LayoutParams) viewPartMain.getLayoutParams();\n        final int viewPartMainWidth = viewPartMain.getMeasuredWidth() + viewPartMainLayoutParams.leftMargin + viewPartMainLayoutParams.rightMargin;\n        final int viewPartMainHeight = viewPartMain.getMeasuredHeight() + viewPartMainLayoutParams.topMargin + viewPartMainLayoutParams.bottomMargin;\n\n        final LayoutParams viewPartInfoLayoutParams = (LayoutParams) viewPartInfo.getLayoutParams();\n        viewPartInfoWidth = viewPartInfo.getMeasuredWidth() + viewPartInfoLayoutParams.leftMargin + viewPartInfoLayoutParams.rightMargin;\n        viewPartInfoHeight = viewPartInfo.getMeasuredHeight() + viewPartInfoLayoutParams.topMargin + viewPartInfoLayoutParams.bottomMargin;\n\n        widthSize = getPaddingLeft() + getPaddingRight();\n        heightSize = getPaddingTop() + getPaddingBottom();\n        if (firstChildId == R.id.media_container) {\n            widthSize += viewPartMainWidth;\n            heightSize += viewPartMainHeight;\n        } else if (firstChildId == R.id.raven_media_container || firstChildId == R.id.profile_container || firstChildId == R.id.voice_media\n                || firstChildId == R.id.story_container || firstChildId == R.id.media_share_container || firstChildId == R.id.link_container\n                || firstChildId == R.id.ivAnimatedMessage || firstChildId == R.id.reel_share_container) {\n            widthSize += viewPartMainWidth;\n            heightSize += viewPartMainHeight + viewPartInfoHeight;\n        } else {\n            int viewPartMainLineCount = 1;\n            float viewPartMainLastLineWidth = 0;\n            final TextView textMessage;\n            if (firstChild instanceof TextView) {\n                textMessage = (TextView) firstChild;\n            }\n            else textMessage = null;\n            if (textMessage != null) {\n                viewPartMainLineCount = textMessage.getLineCount();\n                viewPartMainLastLineWidth = viewPartMainLineCount > 0\n                                            ? textMessage.getLayout().getLineWidth(viewPartMainLineCount - 1)\n                                            : 0;\n                // also include start left padding\n                viewPartMainLastLineWidth += textMessage.getPaddingLeft();\n            }\n\n            final float lastLineWithInfoWidth = viewPartMainLastLineWidth + viewPartInfoWidth;\n            if (viewPartMainLineCount > 1 && lastLineWithInfoWidth <= viewPartMain.getMeasuredWidth()) {\n                widthSize += viewPartMainWidth;\n                heightSize += viewPartMainHeight;\n            } else if (viewPartMainLineCount > 1 && (lastLineWithInfoWidth > availableWidth)) {\n                widthSize += viewPartMainWidth;\n                heightSize += viewPartMainHeight + viewPartInfoHeight;\n            } else if (viewPartMainLineCount == 1 && (viewPartMainWidth + viewPartInfoWidth > availableWidth)) {\n                widthSize += viewPartMain.getMeasuredWidth();\n                heightSize += viewPartMainHeight + viewPartInfoHeight;\n            } else {\n                heightSize += viewPartMainHeight;\n                widthSize += viewPartMainWidth + viewPartInfoWidth;\n            }\n\n            // if (isInEditMode()) {\n            //     TextView wDebugView = (TextView) ((ViewGroup) this.getParent()).findViewWithTag(\"debug\");\n            //     wDebugView.setText(lastLineWithInfoWidth\n            //                                + \"\\n\" + availableWidth\n            //                                + \"\\n\" + viewPartMain.getMeasuredWidth()\n            //                                + \"\\n\" + (lastLineWithInfoWidth <= viewPartMain.getMeasuredWidth())\n            //                                + \"\\n\" + (lastLineWithInfoWidth > availableWidth)\n            //                                + \"\\n\" + (viewPartMainWidth + viewPartInfoWidth > availableWidth));\n            // }\n        }\n        setMeasuredDimension(widthSize, heightSize);\n        super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));\n\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n\n        if (viewPartMain == null || viewPartInfo == null) {\n            return;\n        }\n        // if (withGroupHeader) {\n        //     viewPartMain.layout(\n        //             getPaddingLeft(),\n        //             getPaddingTop() - Utils.convertDpToPx(4),\n        //             viewPartMain.getWidth() + getPaddingLeft(),\n        //             viewPartMain.getHeight() + getPaddingTop());\n        //\n        // } else {\n        viewPartMain.layout(\n                getPaddingLeft(),\n                getPaddingTop(),\n                viewPartMain.getWidth() + getPaddingLeft(),\n                viewPartMain.getHeight() + getPaddingTop());\n\n        // }\n        viewPartInfo.layout(\n                right - left - viewPartInfoWidth - getPaddingRight(),\n                bottom - top - getPaddingBottom() - viewPartInfoHeight,\n                right - left - getPaddingRight(),\n                bottom - top - getPaddingBottom());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/CircularImageView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Color;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.Nullable;\n\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyInflater;\nimport com.facebook.drawee.generic.RoundingParams;\nimport com.facebook.drawee.view.SimpleDraweeView;\n\nimport awais.instagrabber.R;\n\npublic class CircularImageView extends SimpleDraweeView {\n    public CircularImageView(Context context, GenericDraweeHierarchy hierarchy) {\n        super(context);\n        setHierarchy(hierarchy);\n    }\n\n    public CircularImageView(final Context context) {\n        super(context);\n        inflateHierarchy(context, null);\n    }\n\n    public CircularImageView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n        inflateHierarchy(context, attrs);\n    }\n\n    public CircularImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        inflateHierarchy(context, attrs);\n    }\n\n    protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {\n        Resources resources = context.getResources();\n        final RoundingParams roundingParams = RoundingParams.asCircle();\n        GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources)\n                .setRoundingParams(roundingParams)\n                .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);\n        GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);\n        setAspectRatio(builder.getDesiredAspectRatio());\n        setHierarchy(builder.build());\n        setBackgroundResource(R.drawable.shape_oval_light);\n    }\n\n    /* types: 0 clear, 1 green (feed bestie / has story), 2 red (live) */\n    public void setStoriesBorder(final int type) {\n        // private final int borderSize = 8;\n        final int color = type == 2 ? Color.RED : Color.GREEN;\n        RoundingParams roundingParams = getHierarchy().getRoundingParams();\n        if (roundingParams == null) {\n            roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);\n        }\n        roundingParams.setBorder(color, type == 0 ? 0f : 5.0f);\n        getHierarchy().setRoundingParams(roundingParams);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/CommentMentionClickSpan.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.text.TextPaint;\nimport android.text.style.ClickableSpan;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\n\npublic final class CommentMentionClickSpan extends ClickableSpan {\n    @Override\n    public void onClick(@NonNull final View widget) { }\n\n    @Override\n    public void updateDrawState(@NonNull final TextPaint ds) {\n        ds.setColor(ds.linkColor);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/DirectItemContextMenu.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.TimeInterpolator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.widget.ImageView;\nimport android.widget.PopupWindow;\n\nimport androidx.annotation.IdRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.widget.AppCompatEditText;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.util.Pair;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.animations.RoundedRectRevealOutlineProvider;\nimport awais.instagrabber.customviews.emoji.Emoji;\nimport awais.instagrabber.customviews.emoji.ReactionsManager;\nimport awais.instagrabber.databinding.LayoutDirectItemOptionsBinding;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\n\nimport static android.view.View.MeasureSpec.makeMeasureSpec;\n\npublic class DirectItemContextMenu extends PopupWindow {\n    private static final String TAG = DirectItemContextMenu.class.getSimpleName();\n    private static final int DO_NOT_UPDATE_FLAG = -1;\n    private static final int DURATION = 300;\n\n    private final Context context;\n    private final boolean showReactions;\n    private final ReactionsManager reactionsManager;\n    private final int emojiSize;\n    private final int emojiMargin;\n    private final int emojiMarginHalf;\n    private final Rect startRect = new Rect();\n    private final Rect endRect = new Rect();\n    private final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();\n    private final AnimatorListenerAdapter exitAnimationListener;\n    private final TypedValue selectableItemBackgroundBorderless;\n    private final TypedValue selectableItemBackground;\n    private final int dividerHeight;\n    private final int optionHeight;\n    private final int optionPadding;\n    private final int addAdjust;\n    private final boolean hasOptions;\n    private final List<MenuItem> options;\n    private final int widthWithoutReactions;\n\n    private AnimatorSet openCloseAnimator;\n    private Point location;\n    private Point point;\n    private OnReactionClickListener onReactionClickListener;\n    private OnOptionSelectListener onOptionSelectListener;\n    private OnAddReactionClickListener onAddReactionListener;\n\n    public DirectItemContextMenu(@NonNull final Context context, final boolean showReactions, final List<MenuItem> options) {\n        super(context);\n        this.context = context;\n        this.showReactions = showReactions;\n        this.options = options;\n        if (!showReactions && (options == null || options.isEmpty())) {\n            throw new IllegalArgumentException(\"showReactions is set false and options are empty\");\n        }\n        reactionsManager = ReactionsManager.getInstance(context);\n        final Resources resources = context.getResources();\n        emojiSize = resources.getDimensionPixelSize(R.dimen.reaction_picker_emoji_size);\n        emojiMargin = resources.getDimensionPixelSize(R.dimen.reaction_picker_emoji_margin);\n        emojiMarginHalf = emojiMargin / 2;\n        addAdjust = resources.getDimensionPixelSize(R.dimen.reaction_picker_add_padding_adjustment);\n        dividerHeight = resources.getDimensionPixelSize(R.dimen.horizontal_divider_height);\n        optionHeight = resources.getDimensionPixelSize(R.dimen.reaction_picker_option_height);\n        optionPadding = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius);\n        widthWithoutReactions = resources.getDimensionPixelSize(R.dimen.dm_item_context_min_width);\n        exitAnimationListener = new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationEnd(final Animator animation) {\n                openCloseAnimator = null;\n                point = null;\n                getContentView().post(DirectItemContextMenu.super::dismiss);\n            }\n        };\n        selectableItemBackgroundBorderless = new TypedValue();\n        context.getTheme().resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, selectableItemBackgroundBorderless, true);\n        selectableItemBackground = new TypedValue();\n        context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, selectableItemBackground, true);\n        hasOptions = options != null && !options.isEmpty();\n    }\n\n    public void show(@NonNull View rootView, @NonNull final Point location) {\n        final View content = createContentView();\n        content.measure(makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));\n        setup(content);\n        // rootView.getParent().requestDisallowInterceptTouchEvent(true);\n        // final Point correctedLocation = new Point(location.x, location.y - emojiSize * 2);\n        this.location = location;\n        showAtLocation(rootView, Gravity.TOP | Gravity.START, location.x, location.y);\n        // fixPopupLocation(popupWindow, correctedLocation);\n        animateOpen();\n    }\n\n    private void setup(final View content) {\n        setContentView(content);\n        setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        setFocusable(true);\n        setOutsideTouchable(true);\n        setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);\n        setBackgroundDrawable(null);\n    }\n\n    public void setOnOptionSelectListener(final OnOptionSelectListener onOptionSelectListener) {\n        this.onOptionSelectListener = onOptionSelectListener;\n    }\n\n    public void setOnReactionClickListener(final OnReactionClickListener onReactionClickListener) {\n        this.onReactionClickListener = onReactionClickListener;\n    }\n\n    public void setOnAddReactionListener(final OnAddReactionClickListener onAddReactionListener) {\n        this.onAddReactionListener = onAddReactionListener;\n    }\n\n    private void animateOpen() {\n        final View contentView = getContentView();\n        contentView.setVisibility(View.INVISIBLE);\n        contentView.post(() -> {\n            final AnimatorSet openAnim = new AnimatorSet();\n            // Rectangular reveal.\n            final ValueAnimator revealAnim = createOpenCloseOutlineProvider().createRevealAnimator(contentView, false);\n            revealAnim.setDuration(DURATION);\n            revealAnim.setInterpolator(revealInterpolator);\n\n            ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);\n            fadeIn.setDuration(DURATION);\n            fadeIn.setInterpolator(revealInterpolator);\n            fadeIn.addUpdateListener(anim -> {\n                float alpha = (float) anim.getAnimatedValue();\n                contentView.setAlpha(revealAnim.isStarted() ? alpha : 0);\n            });\n            openAnim.play(fadeIn);\n            openAnim.addListener(new AnimatorListenerAdapter() {\n                @Override\n                public void onAnimationEnd(Animator animation) {\n                    contentView.setAlpha(1f);\n                    openCloseAnimator = null;\n                }\n            });\n\n            openCloseAnimator = openAnim;\n            openAnim.playSequentially(revealAnim);\n            contentView.setVisibility(View.VISIBLE);\n            openAnim.start();\n        });\n    }\n\n    protected void animateClose() {\n        endRect.setEmpty();\n        if (openCloseAnimator != null) {\n            openCloseAnimator.cancel();\n        }\n        final View contentView = getContentView();\n        final AnimatorSet closeAnim = new AnimatorSet();\n        // Rectangular reveal (reversed).\n        final ValueAnimator revealAnim = createOpenCloseOutlineProvider().createRevealAnimator(contentView, true);\n        revealAnim.setDuration(DURATION);\n        revealAnim.setInterpolator(revealInterpolator);\n        closeAnim.play(revealAnim);\n\n        ValueAnimator fadeOut = ValueAnimator.ofFloat(contentView.getAlpha(), 0);\n        fadeOut.setDuration(DURATION);\n        fadeOut.setInterpolator(revealInterpolator);\n        fadeOut.addUpdateListener(anim -> {\n            float alpha = (float) anim.getAnimatedValue();\n            contentView.setAlpha(revealAnim.isStarted() ? alpha : contentView.getAlpha());\n        });\n        closeAnim.playTogether(fadeOut);\n        closeAnim.addListener(exitAnimationListener);\n        openCloseAnimator = closeAnim;\n        closeAnim.start();\n    }\n\n    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {\n        final View contentView = getContentView();\n        final int radius = context.getResources().getDimensionPixelSize(R.dimen.dm_message_card_radius_small);\n        // Log.d(TAG, \"createOpenCloseOutlineProvider: \" + locationOnScreen(contentView) + \" \" + contentView.getMeasuredWidth() + \" \" + contentView\n        //         .getMeasuredHeight());\n        if (point == null) {\n            point = locationOnScreen(contentView);\n        }\n        final int left = location.x - point.x;\n        final int top = location.y - point.y;\n        startRect.set(left, top, left, top);\n        endRect.set(0, 0, contentView.getMeasuredWidth(), contentView.getMeasuredHeight());\n        return new RoundedRectRevealOutlineProvider(radius, radius, startRect, endRect);\n    }\n\n    public void dismiss() {\n        animateClose();\n    }\n\n    private View createContentView() {\n        final LayoutInflater layoutInflater = LayoutInflater.from(context);\n        final LayoutDirectItemOptionsBinding binding = LayoutDirectItemOptionsBinding.inflate(layoutInflater, null, false);\n        Pair<View, View> firstLastEmojiView = null;\n        if (showReactions) {\n            firstLastEmojiView = addReactions(layoutInflater, binding.container);\n        }\n        if (hasOptions) {\n            View divider = null;\n            if (showReactions) {\n                if (firstLastEmojiView == null) {\n                    throw new IllegalStateException(\"firstLastEmojiView is null even though reactions were added\");\n                }\n                // add divider if reactions were added\n                divider = addDivider(binding.container,\n                                     firstLastEmojiView.first.getId(),\n                                     firstLastEmojiView.first.getId(),\n                                     firstLastEmojiView.second.getId());\n                ((ConstraintLayout.LayoutParams) firstLastEmojiView.first.getLayoutParams()).bottomToTop = divider.getId();\n            }\n            addOptions(layoutInflater, binding.container, divider);\n        }\n        return binding.getRoot();\n    }\n\n    private Pair<View, View> addReactions(final LayoutInflater layoutInflater, final ConstraintLayout container) {\n        final List<Emoji> reactions = reactionsManager.getReactions();\n        AppCompatImageView prevSquareImageView = null;\n        View firstImageView = null;\n        View lastImageView = null;\n        for (int i = 0; i < reactions.size(); i++) {\n            final Emoji reaction = reactions.get(i);\n            final AppCompatImageView imageView = getEmojiImageView();\n            final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) imageView.getLayoutParams();\n            if (i == 0 && !hasOptions) {\n                // only connect bottom to parent bottom if there are no options\n                layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n            if (i == 0) {\n                layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n                layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;\n                firstImageView = imageView;\n                layoutParams.setMargins(emojiMargin, emojiMargin, emojiMarginHalf, emojiMargin);\n            } else {\n                layoutParams.startToEnd = prevSquareImageView.getId();\n                final ConstraintLayout.LayoutParams prevViewLayoutParams = (ConstraintLayout.LayoutParams) prevSquareImageView.getLayoutParams();\n                prevViewLayoutParams.endToStart = imageView.getId();\n                // always connect the other image view's top and bottom to the first image view top and bottom\n                layoutParams.topToTop = firstImageView.getId();\n                layoutParams.bottomToBottom = firstImageView.getId();\n                layoutParams.setMargins(emojiMarginHalf, emojiMargin, emojiMarginHalf, emojiMargin);\n            }\n            imageView.setImageDrawable(reaction.getDrawable());\n            imageView.setOnClickListener(view -> {\n                if (onReactionClickListener != null) {\n                    onReactionClickListener.onClick(reaction);\n                }\n                dismiss();\n            });\n            container.addView(imageView);\n            prevSquareImageView = imageView;\n        }\n        // add the + icon\n        if (prevSquareImageView != null) {\n            final AppCompatImageView imageView = getEmojiImageView();\n            final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) imageView.getLayoutParams();\n            layoutParams.topToTop = firstImageView.getId();\n            layoutParams.bottomToBottom = firstImageView.getId();\n            layoutParams.startToEnd = prevSquareImageView.getId();\n            final ConstraintLayout.LayoutParams prevViewLayoutParams = (ConstraintLayout.LayoutParams) prevSquareImageView.getLayoutParams();\n            prevViewLayoutParams.endToStart = imageView.getId();\n            layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;\n            layoutParams.setMargins(emojiMarginHalf - addAdjust, emojiMargin - addAdjust, emojiMargin - addAdjust, emojiMargin - addAdjust);\n            imageView.setImageResource(R.drawable.ic_add);\n            imageView.setOnClickListener(view -> {\n                if (onAddReactionListener != null) {\n                    onAddReactionListener.onAdd();\n                }\n                dismiss();\n            });\n            lastImageView = imageView;\n            container.addView(imageView);\n        }\n        return new Pair<>(firstImageView, lastImageView);\n    }\n\n    @NonNull\n    private AppCompatImageView getEmojiImageView() {\n        final AppCompatImageView imageView = new AppCompatImageView(context);\n        final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(emojiSize, emojiSize);\n        imageView.setBackgroundResource(selectableItemBackgroundBorderless.resourceId);\n        imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n        imageView.setId(SquareImageView.generateViewId());\n        imageView.setLayoutParams(layoutParams);\n        return imageView;\n    }\n\n    private void addOptions(final LayoutInflater layoutInflater,\n                            final ConstraintLayout container,\n                            @Nullable final View divider) {\n        View prevOptionView = null;\n        if (!showReactions) {\n            container.getLayoutParams().width = widthWithoutReactions;\n        }\n        for (int i = 0; i < options.size(); i++) {\n            final MenuItem menuItem = options.get(i);\n            final AppCompatTextView textView = getTextView();\n            final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) textView.getLayoutParams();\n            layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;\n            layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;\n            if (i == 0) {\n                if (divider != null) {\n                    layoutParams.topToBottom = divider.getId();\n                    ((ConstraintLayout.LayoutParams) divider.getLayoutParams()).bottomToTop = textView.getId();\n                } else {\n                    // if divider is null mean reactions were not added, so connect top to top of parent\n                    layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n                    layoutParams.topMargin = emojiMargin; // material design spec (https://material.io/components/menus#specs)\n                }\n            } else {\n                layoutParams.topToBottom = prevOptionView.getId();\n                final ConstraintLayout.LayoutParams prevLayoutParams = (ConstraintLayout.LayoutParams) prevOptionView.getLayoutParams();\n                prevLayoutParams.bottomToTop = textView.getId();\n            }\n            if (i == options.size() - 1) {\n                layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n                layoutParams.bottomMargin = emojiMargin; // material design spec (https://material.io/components/menus#specs)\n            }\n            textView.setText(context.getString(menuItem.getTitleRes()));\n            textView.setOnClickListener(v -> {\n                if (onOptionSelectListener != null) {\n                    onOptionSelectListener.onSelect(menuItem.getItemId(), menuItem.getCallback());\n                }\n                dismiss();\n            });\n            container.addView(textView);\n            prevOptionView = textView;\n        }\n    }\n\n    private AppCompatTextView getTextView() {\n        final AppCompatTextView textView = new AppCompatTextView(context);\n        textView.setId(AppCompatEditText.generateViewId());\n        textView.setBackgroundResource(selectableItemBackground.resourceId);\n        textView.setGravity(Gravity.CENTER_VERTICAL);\n        textView.setPaddingRelative(optionPadding, 0, optionPadding, 0);\n        textView.setTextAppearance(context, R.style.TextAppearance_MaterialComponents_Body1);\n        final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,\n                                                                                             optionHeight);\n        textView.setLayoutParams(layoutParams);\n        return textView;\n    }\n\n    private View addDivider(final ConstraintLayout container,\n                            final int topViewId,\n                            final int startViewId,\n                            final int endViewId) {\n        final View dividerView = new View(context);\n        dividerView.setId(View.generateViewId());\n        dividerView.setBackgroundResource(R.drawable.pref_list_divider_material);\n        final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,\n                                                                                             dividerHeight);\n        layoutParams.topToBottom = topViewId;\n        layoutParams.startToStart = startViewId;\n        layoutParams.endToEnd = endViewId;\n        dividerView.setLayoutParams(layoutParams);\n        container.addView(dividerView);\n        return dividerView;\n    }\n\n    @NonNull\n    private Point locationOnScreen(@NonNull final View view) {\n        final int[] location = new int[2];\n        view.getLocationOnScreen(location);\n        return new Point(location[0], location[1]);\n    }\n\n    public static class MenuItem {\n        @IdRes\n        private final int itemId;\n        @StringRes\n        private final int titleRes;\n\n        /**\n         * Callback function\n         */\n        private final Function<DirectItem, Void> callback;\n\n        public MenuItem(@IdRes final int itemId, @StringRes final int titleRes) {\n            this(itemId, titleRes, null);\n        }\n\n        public MenuItem(@IdRes final int itemId, @StringRes final int titleRes, @Nullable final Function<DirectItem, Void> callback) {\n            this.itemId = itemId;\n            this.titleRes = titleRes;\n            this.callback = callback;\n        }\n\n        public int getItemId() {\n            return itemId;\n        }\n\n        public int getTitleRes() {\n            return titleRes;\n        }\n\n        public Function<DirectItem, Void> getCallback() {\n            return callback;\n        }\n    }\n\n    public interface OnOptionSelectListener {\n        void onSelect(int itemId, @Nullable Function<DirectItem, Void> callback);\n    }\n\n    public interface OnReactionClickListener {\n        void onClick(Emoji emoji);\n    }\n\n    public interface OnAddReactionClickListener {\n        void onAdd();\n    }\n\n    // @NonNull\n    // private Rect getGlobalVisibleRect(@NonNull final View view) {\n    //     final Rect rect = new Rect();\n    //     view.getGlobalVisibleRect(rect);\n    //     return rect;\n    // }\n\n    // private void fixPopupLocation(@NonNull final PopupWindow popupWindow, @NonNull final Point desiredLocation) {\n    //     popupWindow.getContentView().post(() -> {\n    //         final Point actualLocation = locationOnScreen(popupWindow.getContentView());\n    //\n    //         if (!(actualLocation.x == desiredLocation.x && actualLocation.y == desiredLocation.y)) {\n    //             final int differenceX = actualLocation.x - desiredLocation.x;\n    //             final int differenceY = actualLocation.y - desiredLocation.y;\n    //\n    //             final int fixedOffsetX;\n    //             final int fixedOffsetY;\n    //\n    //             if (actualLocation.x > desiredLocation.x) {\n    //                 fixedOffsetX = desiredLocation.x - differenceX;\n    //             } else {\n    //                 fixedOffsetX = desiredLocation.x + differenceX;\n    //             }\n    //\n    //             if (actualLocation.y > desiredLocation.y) {\n    //                 fixedOffsetY = desiredLocation.y - differenceY;\n    //             } else {\n    //                 fixedOffsetY = desiredLocation.y + differenceY;\n    //             }\n    //\n    //             popupWindow.update(fixedOffsetX, fixedOffsetY, DO_NOT_UPDATE_FLAG, DO_NOT_UPDATE_FLAG);\n    //         }\n    //     });\n    // }\n}\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/DirectItemFrameLayout.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\npublic class DirectItemFrameLayout extends FrameLayout {\n    private static final String TAG = DirectItemFrameLayout.class.getSimpleName();\n\n    private boolean longPressed = false;\n    private float touchX;\n    private float touchY;\n    private OnItemLongClickListener onItemLongClickListener;\n    private int touchSlop;\n\n    private final Handler handler = new Handler();\n    private final Runnable longPressRunnable = () -> {\n        longPressed = true;\n        if (onItemLongClickListener != null) {\n            onItemLongClickListener.onLongClick(this, touchX, touchY);\n        }\n    };\n    private final Runnable longPressStartRunnable = () -> {\n        if (onItemLongClickListener != null) {\n            onItemLongClickListener.onLongClickStart(this);\n        }\n    };\n\n    public DirectItemFrameLayout(@NonNull final Context context) {\n        super(context);\n        init(context);\n    }\n\n    public DirectItemFrameLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    public DirectItemFrameLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    public DirectItemFrameLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n        init(context);\n    }\n\n    private void init(final Context context) {\n        ViewConfiguration vc = ViewConfiguration.get(context);\n        touchSlop = vc.getScaledTouchSlop();\n    }\n\n    public void setOnItemLongClickListener(final OnItemLongClickListener onItemLongClickListener) {\n        this.onItemLongClickListener = onItemLongClickListener;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(final MotionEvent ev) {\n        switch (ev.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                longPressed = false;\n                handler.postDelayed(longPressRunnable, ViewConfiguration.getLongPressTimeout());\n                handler.postDelayed(longPressStartRunnable, ViewConfiguration.getTapTimeout());\n                touchX = ev.getRawX();\n                touchY = ev.getRawY();\n                break;\n            case MotionEvent.ACTION_MOVE:\n                final float diffX = touchX - ev.getRawX();\n                final float diffXAbs = Math.abs(diffX);\n                final boolean isMoved = diffXAbs > touchSlop || Math.abs(touchY - ev.getRawY()) > touchSlop;\n                if (longPressed || isMoved) {\n                    handler.removeCallbacks(longPressStartRunnable);\n                    handler.removeCallbacks(longPressRunnable);\n                    if (!longPressed) {\n                        if (onItemLongClickListener != null) {\n                            onItemLongClickListener.onLongClickCancel(this);\n                        }\n                    }\n                    // if (diffXAbs > touchSlop) {\n                    //     setTranslationX(-diffX);\n                    // }\n                }\n                break;\n            case MotionEvent.ACTION_UP:\n                handler.removeCallbacks(longPressRunnable);\n                handler.removeCallbacks(longPressStartRunnable);\n                if (longPressed) {\n                    return true;\n                }\n                if (onItemLongClickListener != null) {\n                    onItemLongClickListener.onLongClickCancel(this);\n                }\n                break;\n            case MotionEvent.ACTION_CANCEL:\n                handler.removeCallbacks(longPressRunnable);\n                handler.removeCallbacks(longPressStartRunnable);\n                if (onItemLongClickListener != null) {\n                    onItemLongClickListener.onLongClickCancel(this);\n                }\n                break;\n        }\n        final boolean dispatchTouchEvent = super.dispatchTouchEvent(ev);\n        if (ev.getAction() == MotionEvent.ACTION_DOWN && !dispatchTouchEvent) {\n            return true;\n        }\n        return dispatchTouchEvent;\n    }\n\n    public interface OnItemLongClickListener {\n        void onLongClickStart(View view);\n\n        void onLongClickCancel(View view);\n\n        void onLongClick(View view, float x, float y);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/FixedImageView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.appcompat.widget.AppCompatImageView;\n\npublic final class FixedImageView extends AppCompatImageView {\n    public FixedImageView(final Context context) {\n        super(context);\n    }\n\n    public FixedImageView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public FixedImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected void onMeasure(final int wMeasure, final int hMeasure) {\n        super.onMeasure(wMeasure, wMeasure);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.transition.ChangeBounds;\nimport androidx.transition.Transition;\nimport androidx.transition.TransitionManager;\nimport androidx.transition.TransitionSet;\n\nimport java.time.Duration;\n\nimport awais.instagrabber.customviews.helpers.ChangeText;\nimport awais.instagrabber.utils.NumberUtils;\n\npublic class FormattedNumberTextView extends AppCompatTextView {\n    private static final String TAG = FormattedNumberTextView.class.getSimpleName();\n    private static final Transition TRANSITION;\n\n    private long number = Long.MIN_VALUE;\n    private boolean showAbbreviation = true;\n    private boolean animateChanges = false;\n    private boolean toggleOnClick = true;\n    private boolean autoToggleToAbbreviation = true;\n    private long autoToggleTimeoutMs = Duration.ofSeconds(2).toMillis();\n    private boolean initDone = false;\n\n    static {\n        final TransitionSet transitionSet = new TransitionSet();\n        final ChangeText changeText = new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN);\n        transitionSet.addTransition(changeText).addTransition(new ChangeBounds());\n        TRANSITION = transitionSet;\n    }\n\n\n    public FormattedNumberTextView(@NonNull final Context context) {\n        super(context);\n        init();\n    }\n\n    public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        if (initDone) return;\n        setupClickToggle();\n        initDone = true;\n    }\n\n    private void setupClickToggle() {\n        setOnClickListener(null);\n    }\n\n    private OnClickListener getWrappedClickListener(@Nullable final OnClickListener l) {\n        if (!toggleOnClick) {\n            return l;\n        }\n        return v -> {\n            toggleAbbreviation();\n            if (l != null) {\n                l.onClick(this);\n            }\n        };\n    }\n\n    public void setNumber(final long number) {\n        if (this.number == number) return;\n        this.number = number;\n        format();\n    }\n\n    public void clearNumber() {\n        if (number == Long.MIN_VALUE) return;\n        number = Long.MIN_VALUE;\n        format();\n    }\n\n    public void setShowAbbreviation(final boolean showAbbreviation) {\n        if (this.showAbbreviation && showAbbreviation) return;\n        this.showAbbreviation = showAbbreviation;\n        format();\n    }\n\n    public boolean isShowAbbreviation() {\n        return showAbbreviation;\n    }\n\n    private void toggleAbbreviation() {\n        if (number == Long.MIN_VALUE) return;\n        setShowAbbreviation(!showAbbreviation);\n    }\n\n    public void setToggleOnClick(final boolean toggleOnClick) {\n        this.toggleOnClick = toggleOnClick;\n    }\n\n    public boolean isToggleOnClick() {\n        return toggleOnClick;\n    }\n\n    public void setAutoToggleToAbbreviation(final boolean autoToggleToAbbreviation) {\n        this.autoToggleToAbbreviation = autoToggleToAbbreviation;\n    }\n\n    public boolean isAutoToggleToAbbreviation() {\n        return autoToggleToAbbreviation;\n    }\n\n    public void setAutoToggleTimeoutMs(final long autoToggleTimeoutMs) {\n        this.autoToggleTimeoutMs = autoToggleTimeoutMs;\n    }\n\n    public long getAutoToggleTimeoutMs() {\n        return autoToggleTimeoutMs;\n    }\n\n    public void setAnimateChanges(final boolean animateChanges) {\n        this.animateChanges = animateChanges;\n    }\n\n    public boolean isAnimateChanges() {\n        return animateChanges;\n    }\n\n    @Override\n    public void setOnClickListener(@Nullable final OnClickListener l) {\n        super.setOnClickListener(getWrappedClickListener(l));\n    }\n\n    private void format() {\n        post(() -> {\n            if (animateChanges) {\n                try {\n                    TransitionManager.beginDelayedTransition((ViewGroup) getParent(), TRANSITION);\n                } catch (Exception e) {\n                    Log.e(TAG, \"format: \", e);\n                }\n            }\n            if (number == Long.MIN_VALUE) {\n                setText(null);\n                return;\n            }\n            if (showAbbreviation) {\n                setText(NumberUtils.abbreviate(number, null));\n                return;\n            }\n            setText(String.valueOf(number));\n            if (autoToggleToAbbreviation) {\n                getHandler().postDelayed(() -> setShowAbbreviation(true), autoToggleTimeoutMs);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/InsetsAnimationLinearLayout.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.WindowInsetsAnimation;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.NestedScrollingParent3;\nimport androidx.core.view.NestedScrollingParentHelper;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.Arrays;\n\nimport awais.instagrabber.customviews.helpers.SimpleImeAnimationController;\nimport awais.instagrabber.utils.ViewUtils;\n\nimport static androidx.core.view.ViewCompat.TYPE_TOUCH;\n\npublic final class InsetsAnimationLinearLayout extends LinearLayout implements NestedScrollingParent3 {\n    private final NestedScrollingParentHelper nestedScrollingParentHelper = new NestedScrollingParentHelper(this);\n    private final SimpleImeAnimationController imeAnimController = new SimpleImeAnimationController();\n    private final int[] tempIntArray2 = new int[2];\n    private final int[] startViewLocation = new int[2];\n\n    private View currentNestedScrollingChild;\n    private int dropNextY;\n    private boolean scrollImeOffScreenWhenVisible = true;\n    private boolean scrollImeOnScreenWhenNotVisible = true;\n    private boolean scrollImeOffScreenWhenVisibleOnFling = false;\n    private boolean scrollImeOnScreenWhenNotVisibleOnFling = false;\n\n    public InsetsAnimationLinearLayout(final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public InsetsAnimationLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public final boolean getScrollImeOffScreenWhenVisible() {\n        return scrollImeOffScreenWhenVisible;\n    }\n\n    public final void setScrollImeOffScreenWhenVisible(boolean scrollImeOffScreenWhenVisible) {\n        this.scrollImeOffScreenWhenVisible = scrollImeOffScreenWhenVisible;\n    }\n\n    public final boolean getScrollImeOnScreenWhenNotVisible() {\n        return scrollImeOnScreenWhenNotVisible;\n    }\n\n    public final void setScrollImeOnScreenWhenNotVisible(boolean scrollImeOnScreenWhenNotVisible) {\n        this.scrollImeOnScreenWhenNotVisible = scrollImeOnScreenWhenNotVisible;\n    }\n\n    public boolean getScrollImeOffScreenWhenVisibleOnFling() {\n        return scrollImeOffScreenWhenVisibleOnFling;\n    }\n\n    public void setScrollImeOffScreenWhenVisibleOnFling(final boolean scrollImeOffScreenWhenVisibleOnFling) {\n        this.scrollImeOffScreenWhenVisibleOnFling = scrollImeOffScreenWhenVisibleOnFling;\n    }\n\n    public boolean getScrollImeOnScreenWhenNotVisibleOnFling() {\n        return scrollImeOnScreenWhenNotVisibleOnFling;\n    }\n\n    public void setScrollImeOnScreenWhenNotVisibleOnFling(final boolean scrollImeOnScreenWhenNotVisibleOnFling) {\n        this.scrollImeOnScreenWhenNotVisibleOnFling = scrollImeOnScreenWhenNotVisibleOnFling;\n    }\n\n    public SimpleImeAnimationController getImeAnimController() {\n        return imeAnimController;\n    }\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull final View child,\n                                       @NonNull final View target,\n                                       final int axes,\n                                       final int type) {\n        return (axes & SCROLL_AXIS_VERTICAL) != 0 && type == TYPE_TOUCH;\n    }\n\n    @Override\n    public void onNestedScrollAccepted(@NonNull final View child,\n                                       @NonNull final View target,\n                                       final int axes,\n                                       final int type) {\n        nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type);\n        currentNestedScrollingChild = child;\n    }\n\n    @Override\n    public void onNestedPreScroll(@NonNull final View target,\n                                  final int dx,\n                                  final int dy,\n                                  @NonNull final int[] consumed,\n                                  final int type) {\n        if (imeAnimController.isInsetAnimationRequestPending()) {\n            consumed[0] = dx;\n            consumed[1] = dy;\n        } else {\n            int deltaY = dy;\n            if (dropNextY != 0) {\n                consumed[1] = dropNextY;\n                deltaY = dy - dropNextY;\n                dropNextY = 0;\n            }\n\n            if (deltaY < 0) {\n                if (imeAnimController.isInsetAnimationInProgress()) {\n                    consumed[1] -= imeAnimController.insetBy(-deltaY);\n                } else if (scrollImeOffScreenWhenVisible && !imeAnimController.isInsetAnimationRequestPending()) {\n                    WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this);\n                    if (rootWindowInsets != null) {\n                        if (rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) {\n                            startControlRequest();\n                            consumed[1] = deltaY;\n                        }\n                    }\n                }\n            }\n\n        }\n    }\n\n    @Override\n    public void onNestedScroll(@NonNull final View target,\n                               final int dxConsumed,\n                               final int dyConsumed,\n                               final int dxUnconsumed,\n                               final int dyUnconsumed,\n                               final int type,\n                               @NonNull final int[] consumed) {\n        if (dyUnconsumed > 0) {\n            if (imeAnimController.isInsetAnimationInProgress()) {\n                consumed[1] = -imeAnimController.insetBy(-dyUnconsumed);\n            } else if (scrollImeOnScreenWhenNotVisible && !imeAnimController.isInsetAnimationRequestPending()) {\n                WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this);\n                if (rootWindowInsets != null) {\n                    if (!rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) {\n                        startControlRequest();\n                        consumed[1] = dyUnconsumed;\n                    }\n                }\n            }\n        }\n\n    }\n\n    @Override\n    public boolean onNestedFling(@NonNull final View target,\n                                 final float velocityX,\n                                 final float velocityY,\n                                 final boolean consumed) {\n        if (imeAnimController.isInsetAnimationInProgress()) {\n            imeAnimController.animateToFinish(velocityY);\n            return true;\n        } else {\n            boolean imeVisible = false;\n            final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this);\n            if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) {\n                imeVisible = true;\n            }\n            if (velocityY > 0 && scrollImeOnScreenWhenNotVisibleOnFling && !imeVisible) {\n                imeAnimController.startAndFling(this, velocityY);\n                return true;\n            } else if (velocityY < 0 && scrollImeOffScreenWhenVisibleOnFling && imeVisible) {\n                imeAnimController.startAndFling(this, velocityY);\n                return true;\n            } else {\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public void onStopNestedScroll(@NonNull final View target, final int type) {\n        nestedScrollingParentHelper.onStopNestedScroll(target, type);\n        if (imeAnimController.isInsetAnimationInProgress() && !imeAnimController.isInsetAnimationFinishing()) {\n            imeAnimController.animateToFinish(null);\n        }\n        reset();\n    }\n\n    @Override\n    public void dispatchWindowInsetsAnimationPrepare(@NonNull final WindowInsetsAnimation animation) {\n        super.dispatchWindowInsetsAnimationPrepare(animation);\n        ViewUtils.suppressLayoutCompat(this, false);\n    }\n\n    private void startControlRequest() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {\n            return;\n        }\n        ViewUtils.suppressLayoutCompat(this, true);\n        if (currentNestedScrollingChild != null) {\n            currentNestedScrollingChild.getLocationInWindow(startViewLocation);\n        }\n        imeAnimController.startControlRequest(this, windowInsetsAnimationControllerCompat -> onControllerReady());\n    }\n\n    private void onControllerReady() {\n        if (currentNestedScrollingChild != null) {\n            imeAnimController.insetBy(0);\n            int[] location = tempIntArray2;\n            currentNestedScrollingChild.getLocationInWindow(location);\n            dropNextY = location[1] - startViewLocation[1];\n        }\n\n    }\n\n    private void reset() {\n        dropNextY = 0;\n        Arrays.fill(startViewLocation, 0);\n        ViewUtils.suppressLayoutCompat(this, false);\n    }\n\n    @Override\n    public void onNestedScrollAccepted(@NonNull final View child,\n                                       @NonNull final View target,\n                                       final int axes) {\n        onNestedScrollAccepted(child, target, axes, TYPE_TOUCH);\n    }\n\n    @Override\n    public void onNestedScroll(@NonNull final View target,\n                               final int dxConsumed,\n                               final int dyConsumed,\n                               final int dxUnconsumed,\n                               final int dyUnconsumed,\n                               final int type) {\n        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, tempIntArray2);\n    }\n\n    @Override\n    public void onStopNestedScroll(@NonNull final View target) {\n        onStopNestedScroll(target, TYPE_TOUCH);\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/InsetsNotifyingCoordinatorLayout.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.WindowInsets;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\npublic class InsetsNotifyingCoordinatorLayout extends CoordinatorLayout {\n\n    public InsetsNotifyingCoordinatorLayout(@NonNull final Context context) {\n        super(context);\n    }\n\n    public InsetsNotifyingCoordinatorLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public InsetsNotifyingCoordinatorLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public WindowInsets onApplyWindowInsets(WindowInsets insets) {\n        int childCount = getChildCount();\n        for (int index = 0; index < childCount; index++) {\n            getChildAt(index).dispatchApplyWindowInsets(insets);\n        }\n        return insets;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/InsetsNotifyingLinearLayout.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.WindowInsets;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.Nullable;\n\npublic class InsetsNotifyingLinearLayout extends LinearLayout {\n    public InsetsNotifyingLinearLayout(final Context context) {\n        super(context);\n    }\n\n    public InsetsNotifyingLinearLayout(final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public InsetsNotifyingLinearLayout(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public InsetsNotifyingLinearLayout(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    @Override\n    public WindowInsets onApplyWindowInsets(WindowInsets insets) {\n        int childCount = getChildCount();\n        for (int index = 0; index < childCount; index++) {\n            getChildAt(index).dispatchApplyWindowInsets(insets);\n        }\n        return insets;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/KeyNotifyingEmojiEditText.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.KeyEvent;\n\nimport androidx.emoji.widget.EmojiEditText;\n\npublic class KeyNotifyingEmojiEditText extends EmojiEditText {\n    private OnKeyEventListener onKeyEventListener;\n\n    public KeyNotifyingEmojiEditText(final Context context) {\n        super(context);\n    }\n\n    public KeyNotifyingEmojiEditText(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public KeyNotifyingEmojiEditText(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public KeyNotifyingEmojiEditText(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    @Override\n    public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {\n        if (onKeyEventListener != null) {\n            final boolean listenerResult = onKeyEventListener.onKeyPreIme(keyCode, event);\n            if (listenerResult) return true;\n        }\n        return super.onKeyPreIme(keyCode, event);\n    }\n\n    public void setOnKeyEventListener(final OnKeyEventListener onKeyEventListener) {\n        this.onKeyEventListener = onKeyEventListener;\n    }\n\n    public interface OnKeyEventListener {\n        boolean onKeyPreIme(int keyCode, KeyEvent keyEvent);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/MouseDrawer.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Matrix;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.InputDevice;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.customview.view.AbsSavedState;\nimport androidx.customview.widget.ViewDragHelper;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.BuildConfig;\n\n// exactly same as the LayoutDrawer with some edits\n@SuppressLint(\"RtlHardcoded\")\npublic class MouseDrawer extends ViewGroup {\n    @IntDef({ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING})\n    @Retention(RetentionPolicy.SOURCE)\n    private @interface State {}\n\n    @IntDef(value = {Gravity.NO_GRAVITY, Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END}, flag = true)\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface EdgeGravity {}\n\n    ////////////////////////////////////////////////////////////////////////////////////\n    private static final boolean CHILDREN_DISALLOW_INTERCEPT = true;\n    ////////////////////////////////////////////////////////////////////////////////////\n    private final ArrayList<View> mNonDrawerViews = new ArrayList<>();\n    private final ViewDragHelper mLeftDragger, mRightDragger;\n    private boolean mInLayout, mFirstLayout = true;\n    private float mDrawerElevation, mInitialMotionX, mInitialMotionY;\n    private int mDrawerState;\n    private List<DrawerListener> mListeners;\n    private Matrix mChildInvertedMatrix;\n    private Rect mChildHitRect;\n\n    public interface DrawerListener {\n        void onDrawerSlide(final View drawerView, @EdgeGravity final int gravity, final float slideOffset);\n        default void onDrawerOpened(final View drawerView, @EdgeGravity final int gravity) {}\n        default void onDrawerClosed(final View drawerView, @EdgeGravity final int gravity) {}\n        default void onDrawerStateChanged() {}\n    }\n\n    public MouseDrawer(@NonNull final Context context) {\n        this(context, null);\n    }\n\n    public MouseDrawer(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MouseDrawer(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {\n        super(context, attrs, defStyle);\n        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);\n\n        final float density = getResources().getDisplayMetrics().density;\n        this.mDrawerElevation = 10 * density;\n\n        final float touchSlopSensitivity = 0.5f; // was 1.0f\n        final float minFlingVelocity = 400 /* dips per second */ * density;\n\n        final ViewDragCallback mLeftCallback = new ViewDragCallback(Gravity.LEFT);\n        this.mLeftDragger = ViewDragHelper.create(this, touchSlopSensitivity, mLeftCallback);\n        this.mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);\n        this.mLeftDragger.setMinVelocity(minFlingVelocity);\n\n        final ViewDragCallback mRightCallback = new ViewDragCallback(Gravity.RIGHT);\n        this.mRightDragger = ViewDragHelper.create(this, touchSlopSensitivity, mRightCallback);\n        this.mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);\n        this.mRightDragger.setMinVelocity(minFlingVelocity);\n\n        try {\n            final Field edgeSizeField = ViewDragHelper.class.getDeclaredField(\"mEdgeSize\");\n            if (!edgeSizeField.isAccessible()) edgeSizeField.setAccessible(true);\n            final int widthPixels = getResources().getDisplayMetrics().widthPixels; // whole screen\n            edgeSizeField.set(this.mLeftDragger, widthPixels / 2);\n            edgeSizeField.set(this.mRightDragger, widthPixels / 2);\n        } catch (final Exception e) {\n            if (BuildConfig.DEBUG) Log.e(\"AWAISKING_APP\", \"\", e);\n        }\n\n        mLeftCallback.setDragger(mLeftDragger);\n        mRightCallback.setDragger(mRightDragger);\n\n        setFocusableInTouchMode(true);\n        //setMotionEventSplittingEnabled(false);\n    }\n\n    public void setDrawerElevation(final float elevation) {\n        mDrawerElevation = elevation;\n        for (int i = 0; i < getChildCount(); i++) {\n            final View child = getChildAt(i);\n            if (isDrawerView(child)) ViewCompat.setElevation(child, mDrawerElevation);\n        }\n    }\n\n    public float getDrawerElevation() {\n        return Build.VERSION.SDK_INT >= 21 ? mDrawerElevation : 0f;\n    }\n\n    public void addDrawerListener(@NonNull final DrawerListener listener) {\n        if (mListeners == null) mListeners = new ArrayList<>();\n        mListeners.add(listener);\n    }\n\n    private boolean isInBoundsOfChild(final float x, final float y, final View child) {\n        if (mChildHitRect == null) mChildHitRect = new Rect();\n        child.getHitRect(mChildHitRect);\n        return mChildHitRect.contains((int) x, (int) y);\n    }\n\n    private boolean dispatchTransformedGenericPointerEvent(final MotionEvent event, @NonNull final View child) {\n        final boolean handled;\n        final Matrix childMatrix = child.getMatrix();\n        if (!childMatrix.isIdentity()) {\n            final MotionEvent transformedEvent = getTransformedMotionEvent(event, child);\n            handled = child.dispatchGenericMotionEvent(transformedEvent);\n            transformedEvent.recycle();\n        } else {\n            final float offsetX = getScrollX() - child.getLeft();\n            final float offsetY = getScrollY() - child.getTop();\n            event.offsetLocation(offsetX, offsetY);\n            handled = child.dispatchGenericMotionEvent(event);\n            event.offsetLocation(-offsetX, -offsetY);\n        }\n        return handled;\n    }\n\n    @NonNull\n    private MotionEvent getTransformedMotionEvent(final MotionEvent event, @NonNull final View child) {\n        final float offsetX = getScrollX() - child.getLeft();\n        final float offsetY = getScrollY() - child.getTop();\n        final MotionEvent transformedEvent = MotionEvent.obtain(event);\n        transformedEvent.offsetLocation(offsetX, offsetY);\n        final Matrix childMatrix = child.getMatrix();\n        if (!childMatrix.isIdentity()) {\n            if (mChildInvertedMatrix == null) mChildInvertedMatrix = new Matrix();\n            childMatrix.invert(mChildInvertedMatrix);\n            transformedEvent.transform(mChildInvertedMatrix);\n        }\n        return transformedEvent;\n    }\n\n    void updateDrawerState(@State final int activeState, final View activeDrawer) {\n        final int leftState = mLeftDragger.getViewDragState();\n        final int rightState = mRightDragger.getViewDragState();\n\n        final int state;\n        if (leftState == ViewDragHelper.STATE_DRAGGING || rightState == ViewDragHelper.STATE_DRAGGING)\n            state = ViewDragHelper.STATE_DRAGGING;\n        else if (leftState == ViewDragHelper.STATE_SETTLING || rightState == ViewDragHelper.STATE_SETTLING)\n            state = ViewDragHelper.STATE_SETTLING;\n        else state = ViewDragHelper.STATE_IDLE;\n\n        if (activeDrawer != null && activeState == ViewDragHelper.STATE_IDLE) {\n            final LayoutParams lp = (LayoutParams) activeDrawer.getLayoutParams();\n            if (lp.onScreen == 0) dispatchOnDrawerClosed(activeDrawer);\n            else if (lp.onScreen == 1) dispatchOnDrawerOpened(activeDrawer);\n        }\n\n        if (state != mDrawerState) {\n            mDrawerState = state;\n\n            if (mListeners != null) {\n                final int listenerCount = mListeners.size();\n                for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerStateChanged();\n            }\n        }\n    }\n\n    void dispatchOnDrawerClosed(@NonNull final View drawerView) {\n        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();\n        if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 1) {\n            lp.openState = 0;\n\n            if (mListeners != null) {\n                final int listenerCount = mListeners.size();\n                for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerClosed(drawerView, lp.gravity);\n            }\n        }\n    }\n\n    void dispatchOnDrawerOpened(@NonNull final View drawerView) {\n        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();\n        if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 0) {\n            lp.openState = LayoutParams.FLAG_IS_OPENED;\n            if (mListeners != null) {\n                final int listenerCount = mListeners.size();\n                for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerOpened(drawerView, lp.gravity);\n            }\n        }\n    }\n\n    void setDrawerViewOffset(@NonNull final View drawerView, final float slideOffset) {\n        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();\n        if (slideOffset != lp.onScreen) {\n            lp.onScreen = slideOffset;\n\n            if (mListeners != null) {\n                final int listenerCount = mListeners.size();\n                for (int i = listenerCount - 1; i >= 0; i--)\n                    mListeners.get(i).onDrawerSlide(drawerView, lp.gravity, slideOffset);\n            }\n        }\n    }\n\n    float getDrawerViewOffset(@NonNull final View drawerView) {\n        return ((LayoutParams) drawerView.getLayoutParams()).onScreen;\n    }\n\n    int getDrawerViewAbsoluteGravity(@NonNull final View drawerView) {\n        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;\n        return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));\n    }\n\n    boolean checkDrawerViewAbsoluteGravity(final View drawerView, final int checkFor) {\n        final int absGravity = getDrawerViewAbsoluteGravity(drawerView);\n        return (absGravity & checkFor) == checkFor;\n    }\n\n    void moveDrawerToOffset(final View drawerView, final float slideOffset) {\n        final float oldOffset = getDrawerViewOffset(drawerView);\n        final int width = drawerView.getWidth();\n        final int oldPos = (int) (width * oldOffset);\n        final int newPos = (int) (width * slideOffset);\n        final int dx = newPos - oldPos;\n\n        drawerView.offsetLeftAndRight(checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT) ? dx : -dx);\n        setDrawerViewOffset(drawerView, slideOffset);\n    }\n\n    public View findOpenDrawer() {\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();\n            if ((childLp.openState & LayoutParams.FLAG_IS_OPENED) == 1) return child;\n        }\n        return null;\n    }\n\n    public View findDrawerWithGravity(final int gravity) {\n        final int absHorizGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)) & Gravity.HORIZONTAL_GRAVITY_MASK;\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            final int childAbsGravity = getDrawerViewAbsoluteGravity(child);\n            if ((childAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == absHorizGravity) return child;\n        }\n        return null;\n    }\n\n    @NonNull\n    static String gravityToString(@EdgeGravity final int gravity) {\n        if ((gravity & Gravity.LEFT) == Gravity.LEFT) return \"LEFT\";\n        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) return \"RIGHT\";\n        return Integer.toHexString(gravity);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mFirstLayout = true;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        mFirstLayout = true;\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    @Override\n    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\n        setMeasuredDimension(widthSize, heightSize);\n\n        boolean hasDrawerOnLeftEdge = false;\n        boolean hasDrawerOnRightEdge = false;\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n\n            if (child.getVisibility() != GONE) {\n                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n\n                if (isContentView(child)) {\n                    // Content views get measured at exactly the layout's size.\n                    final int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);\n                    final int contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);\n                    child.measure(contentWidthSpec, contentHeightSpec);\n\n                } else if (isDrawerView(child)) {\n                    if (Build.VERSION.SDK_INT >= 21 && ViewCompat.getElevation(child) != mDrawerElevation)\n                        ViewCompat.setElevation(child, mDrawerElevation);\n                    final int childGravity = getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;\n\n                    final boolean isLeftEdgeDrawer = (childGravity == Gravity.LEFT);\n                    if (isLeftEdgeDrawer && hasDrawerOnLeftEdge || !isLeftEdgeDrawer && hasDrawerOnRightEdge)\n                        throw new IllegalStateException(\"Child drawer has absolute gravity \" + gravityToString(childGravity)\n                                + \" but this MouseDrawer already has a drawer view along that edge\");\n\n                    if (isLeftEdgeDrawer) hasDrawerOnLeftEdge = true;\n                    else hasDrawerOnRightEdge = true;\n\n                    final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin, lp.width);\n                    final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);\n                    child.measure(drawerWidthSpec, drawerHeightSpec);\n                } else\n                    throw new IllegalStateException(\"Child \" + child + \" at index \" + i\n                            + \" does not have a valid layout_gravity - must be Gravity.LEFT, Gravity.RIGHT or Gravity.NO_GRAVITY\");\n            }\n        }\n    }\n\n    @Override\n    protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {\n        mInLayout = true;\n        final int width = right - left;\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n\n            if (child.getVisibility() != GONE) {\n                final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n\n                if (isContentView(child)) {\n                    child.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(),\n                            lp.topMargin + child.getMeasuredHeight());\n\n                } else { // Drawer, if it wasn't onMeasure would have thrown an exception.\n                    final int childWidth = child.getMeasuredWidth();\n                    final int childHeight = child.getMeasuredHeight();\n                    final int childLeft;\n                    final float newOffset;\n\n                    if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {\n                        childLeft = -childWidth + (int) (childWidth * lp.onScreen);\n                        newOffset = (float) (childWidth + childLeft) / childWidth;\n                    } else { // Right; onMeasure checked for us.\n                        childLeft = width - (int) (childWidth * lp.onScreen);\n                        newOffset = (float) (width - childLeft) / childWidth;\n                    }\n\n                    final boolean changeOffset = newOffset != lp.onScreen;\n\n                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\n                    switch (vgrav) {\n                        default:\n                        case Gravity.TOP:\n                            child.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);\n                            break;\n\n                        case Gravity.BOTTOM: {\n                            final int height = bottom - top;\n                            child.layout(childLeft, height - lp.bottomMargin - child.getMeasuredHeight(),\n                                    childLeft + childWidth, height - lp.bottomMargin);\n                            break;\n                        }\n\n                        case Gravity.CENTER_VERTICAL: {\n                            final int height = bottom - top;\n                            int childTop = (height - childHeight) / 2;\n\n                            if (childTop < lp.topMargin) childTop = lp.topMargin;\n                            else if (childTop + childHeight > height - lp.bottomMargin)\n                                childTop = height - lp.bottomMargin - childHeight;\n\n                            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);\n                            break;\n                        }\n                    }\n\n                    if (changeOffset) setDrawerViewOffset(child, newOffset);\n\n                    final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE;\n                    if (child.getVisibility() != newVisibility) child.setVisibility(newVisibility);\n                }\n            }\n        }\n        mInLayout = false;\n        mFirstLayout = false;\n    }\n\n    @Override\n    public void requestLayout() {\n        if (!mInLayout) super.requestLayout();\n    }\n\n    @Override\n    public void computeScroll() {\n        final boolean leftDraggerSettling = mLeftDragger.continueSettling(true);\n        final boolean rightDraggerSettling = mRightDragger.continueSettling(true);\n        if (leftDraggerSettling || rightDraggerSettling) postInvalidateOnAnimation();\n    }\n\n    private static boolean hasOpaqueBackground(@NonNull final View v) {\n        final Drawable bg = v.getBackground();\n        if (bg != null) return bg.getOpacity() == PixelFormat.OPAQUE;\n        return false;\n    }\n\n    @Override\n    protected boolean drawChild(@NonNull final Canvas canvas, final View child, final long drawingTime) {\n        final int height = getHeight();\n        final boolean drawingContent = isContentView(child);\n        int clipLeft = 0, clipRight = getWidth();\n\n        final int restoreCount = canvas.save();\n        if (drawingContent) {\n            final int childCount = getChildCount();\n            for (int i = 0; i < childCount; i++) {\n                final View v = getChildAt(i);\n                if (v != child && v.getVisibility() == VISIBLE && hasOpaqueBackground(v) && isDrawerView(v) && v.getHeight() >= height) {\n                    if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {\n                        final int vright = v.getRight();\n                        if (vright > clipLeft) clipLeft = vright;\n                    } else {\n                        final int vleft = v.getLeft();\n                        if (vleft < clipRight) clipRight = vleft;\n                    }\n                }\n            }\n            canvas.clipRect(clipLeft, 0, clipRight, getHeight());\n        }\n\n        final boolean result = super.drawChild(canvas, child, drawingTime);\n        canvas.restoreToCount(restoreCount);\n\n        return result;\n    }\n\n    boolean isContentView(@NonNull final View child) {\n        return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;\n    }\n\n    boolean isDrawerView(@NonNull final View child) {\n        final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;\n        final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(child));\n        return (absGravity & Gravity.LEFT) != 0 || (absGravity & Gravity.RIGHT) != 0;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(@NonNull final MotionEvent ev) {\n        final int action = ev.getActionMasked();\n\n        // \"|\" used deliberately here; both methods should be invoked.\n        final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev) | mRightDragger.shouldInterceptTouchEvent(ev);\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mInitialMotionX = ev.getX();\n                mInitialMotionY = ev.getY();\n                break;\n\n            case MotionEvent.ACTION_MOVE:\n                mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL);\n                break;\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                closeDrawers(true);\n        }\n\n        return interceptForDrag || hasPeekingDrawer();\n    }\n\n    @Override\n    public boolean dispatchGenericMotionEvent(@NonNull final MotionEvent event) {\n        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0 || event.getAction() == MotionEvent.ACTION_HOVER_EXIT)\n            return super.dispatchGenericMotionEvent(event);\n\n        final int childrenCount = getChildCount();\n        if (childrenCount != 0) {\n            final float x = event.getX();\n            final float y = event.getY();\n\n            // Walk through children from top to bottom.\n            for (int i = childrenCount - 1; i >= 0; i--) {\n                final View child = getChildAt(i);\n                if (isInBoundsOfChild(x, y, child) && !isContentView(child) && dispatchTransformedGenericPointerEvent(event, child))\n                    return true;\n            }\n        }\n\n        return false;\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouchEvent(final MotionEvent ev) {\n        mLeftDragger.processTouchEvent(ev);\n        mRightDragger.processTouchEvent(ev);\n\n        final int action = ev.getActionMasked();\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mInitialMotionX = ev.getX();\n                mInitialMotionY = ev.getY();\n                break;\n\n            case MotionEvent.ACTION_UP:\n                final float x = ev.getX();\n                final float y = ev.getY();\n\n                boolean peekingOnly = true;\n                final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);\n                if (touchedView != null && isContentView(touchedView)) {\n                    final float dx = x - mInitialMotionX;\n                    final float dy = y - mInitialMotionY;\n                    final int slop = mLeftDragger.getTouchSlop();\n                    if (dx * dx + dy * dy < slop * slop) {\n                        // Taps close a dimmed open drawer but only if it isn't locked open.\n                        final View openDrawer = findOpenDrawer();\n                        if (openDrawer != null) peekingOnly = false;\n                    }\n                }\n                closeDrawers(peekingOnly);\n                break;\n\n            case MotionEvent.ACTION_CANCEL:\n                closeDrawers(true);\n                break;\n        }\n\n        return true;\n    }\n\n    @Override\n    public void requestDisallowInterceptTouchEvent(final boolean disallowIntercept) {\n        if (CHILDREN_DISALLOW_INTERCEPT || (!mLeftDragger.isEdgeTouched(ViewDragHelper.EDGE_LEFT) && !mRightDragger.isEdgeTouched(ViewDragHelper.EDGE_RIGHT)))\n            super.requestDisallowInterceptTouchEvent(disallowIntercept);\n        if (disallowIntercept) closeDrawers(true);\n    }\n\n    public void closeDrawers() {\n        closeDrawers(false);\n    }\n\n    void closeDrawers(final boolean peekingOnly) {\n        boolean needsInvalidate = false;\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n\n            if (isDrawerView(child) && (!peekingOnly || lp.isPeeking)) {\n                final int childWidth = child.getWidth();\n\n                if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT))\n                    needsInvalidate |= mLeftDragger.smoothSlideViewTo(child, -childWidth, child.getTop());\n                else\n                    needsInvalidate |= mRightDragger.smoothSlideViewTo(child, getWidth(), child.getTop());\n\n                lp.isPeeking = false;\n            }\n        }\n\n        if (needsInvalidate) invalidate();\n    }\n\n    public void openDrawer(@NonNull final View drawerView, final boolean animate) {\n        if (isDrawerView(drawerView)) {\n            final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();\n\n            if (mFirstLayout) {\n                lp.onScreen = 1.f;\n                lp.openState = LayoutParams.FLAG_IS_OPENED;\n            } else if (animate) {\n                lp.openState |= LayoutParams.FLAG_IS_OPENING;\n\n                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))\n                    mLeftDragger.smoothSlideViewTo(drawerView, 0, drawerView.getTop());\n                else\n                    mRightDragger.smoothSlideViewTo(drawerView, getWidth() - drawerView.getWidth(), drawerView.getTop());\n            } else {\n                moveDrawerToOffset(drawerView, 1.f);\n                updateDrawerState(ViewDragHelper.STATE_IDLE, drawerView);\n                drawerView.setVisibility(VISIBLE);\n            }\n\n            invalidate();\n            return;\n        }\n        throw new IllegalArgumentException(\"View \" + drawerView + \" is not a sliding drawer\");\n    }\n\n    public void openDrawer(@NonNull final View drawerView) {\n        openDrawer(drawerView, true);\n    }\n\n    // public void openDrawer(@EdgeGravity final int gravity, final boolean animate) {\n    //     final View drawerView = findDrawerWithGravity(gravity);\n    //     if (drawerView != null) openDrawer(drawerView, animate);\n    //     else throw new IllegalArgumentException(\"No drawer view found with gravity \" + gravityToString(gravity));\n    // }\n\n    // public void openDrawer(@EdgeGravity final int gravity) {\n    //     openDrawer(gravity, true);\n    // }\n\n    public void closeDrawer(@NonNull final View drawerView) {\n        closeDrawer(drawerView, true);\n    }\n\n    public void closeDrawer(@NonNull final View drawerView, final boolean animate) {\n        if (isDrawerView(drawerView)) {\n            final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();\n            if (mFirstLayout) {\n                lp.onScreen = 0.f;\n                lp.openState = 0;\n            } else if (animate) {\n                lp.openState |= LayoutParams.FLAG_IS_CLOSING;\n\n                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))\n                    mLeftDragger.smoothSlideViewTo(drawerView, -drawerView.getWidth(), drawerView.getTop());\n                else\n                    mRightDragger.smoothSlideViewTo(drawerView, getWidth(), drawerView.getTop());\n            } else {\n                moveDrawerToOffset(drawerView, 0.f);\n                updateDrawerState(ViewDragHelper.STATE_IDLE, drawerView);\n                drawerView.setVisibility(INVISIBLE);\n            }\n            invalidate();\n        } else throw new IllegalArgumentException(\"View \" + drawerView + \" is not a sliding drawer\");\n    }\n\n    // public void closeDrawer(@EdgeGravity final int gravity) {\n    //     closeDrawer(gravity, true);\n    // }\n\n    // public void closeDrawer(@EdgeGravity final int gravity, final boolean animate) {\n    //     final View drawerView = findDrawerWithGravity(gravity);\n    //     if (drawerView != null) closeDrawer(drawerView, animate);\n    //     else throw new IllegalArgumentException(\"No drawer view found with gravity \" + gravityToString(gravity));\n    // }\n\n    public boolean isDrawerOpen(@NonNull final View drawer) {\n        if (isDrawerView(drawer)) return (((LayoutParams) drawer.getLayoutParams()).openState & LayoutParams.FLAG_IS_OPENED) == 1;\n        else throw new IllegalArgumentException(\"View \" + drawer + \" is not a drawer\");\n    }\n\n    // public boolean isDrawerOpen(@EdgeGravity final int drawerGravity) {\n    //     final View drawerView = findDrawerWithGravity(drawerGravity);\n    //     return drawerView != null && isDrawerOpen(drawerView);\n    // }\n\n    public boolean isDrawerVisible(@NonNull final View drawer) {\n        if (isDrawerView(drawer)) return ((LayoutParams) drawer.getLayoutParams()).onScreen > 0;\n        throw new IllegalArgumentException(\"View \" + drawer + \" is not a drawer\");\n    }\n\n    // public boolean isDrawerVisible(@EdgeGravity final int drawerGravity) {\n    //     final View drawerView = findDrawerWithGravity(drawerGravity);\n    //     return drawerView != null && isDrawerVisible(drawerView);\n    // }\n\n    private boolean hasPeekingDrawer() {\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();\n            if (lp.isPeeking) return true;\n        }\n        return false;\n    }\n\n    @Override\n    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {\n        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n    }\n\n    @Override\n    protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams params) {\n        return params instanceof LayoutParams ? new LayoutParams((LayoutParams) params) :\n                params instanceof ViewGroup.MarginLayoutParams ? new LayoutParams((MarginLayoutParams) params) : new LayoutParams(params);\n    }\n\n    @Override\n    protected boolean checkLayoutParams(final ViewGroup.LayoutParams params) {\n        return params instanceof LayoutParams && super.checkLayoutParams(params);\n    }\n\n    @Override\n    public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {\n        return new LayoutParams(getContext(), attrs);\n    }\n\n    @Override\n    public void addFocusables(final ArrayList<View> views, final int direction, final int focusableMode) {\n        if (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) {\n            final int childCount = getChildCount();\n            boolean isDrawerOpen = false;\n            for (int i = 0; i < childCount; i++) {\n                final View child = getChildAt(i);\n                if (!isDrawerView(child)) mNonDrawerViews.add(child);\n                else if (isDrawerOpen(child)) {\n                    isDrawerOpen = true;\n                    child.addFocusables(views, direction, focusableMode);\n                }\n            }\n\n            if (!isDrawerOpen) {\n                final int nonDrawerViewsCount = mNonDrawerViews.size();\n                for (int i = 0; i < nonDrawerViewsCount; ++i) {\n                    final View child = mNonDrawerViews.get(i);\n                    if (child.getVisibility() == View.VISIBLE) child.addFocusables(views, direction, focusableMode);\n                }\n            }\n\n            mNonDrawerViews.clear();\n        }\n    }\n\n    private boolean hasVisibleDrawer() {\n        return findVisibleDrawer() != null;\n    }\n\n    @Nullable\n    final View findVisibleDrawer() {\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            if (isDrawerView(child) && isDrawerVisible(child)) return child;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean onKeyDown(final int keyCode, final KeyEvent event) {\n        if (keyCode == KeyEvent.KEYCODE_BACK && hasVisibleDrawer()) {\n            event.startTracking();\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n\n    @Override\n    public boolean onKeyUp(final int keyCode, final KeyEvent event) {\n        if (keyCode == KeyEvent.KEYCODE_BACK) {\n            final View visibleDrawer = findVisibleDrawer();\n            if (visibleDrawer != null && isDrawerView(visibleDrawer)) closeDrawers();\n            return visibleDrawer != null;\n        }\n        return super.onKeyUp(keyCode, event);\n    }\n\n    @Override\n    protected void onRestoreInstanceState(final Parcelable state) {\n        if (state instanceof SavedState) {\n            final SavedState ss = (SavedState) state;\n            super.onRestoreInstanceState(ss.getSuperState());\n\n            if (ss.openDrawerGravity != Gravity.NO_GRAVITY) {\n                final View toOpen = findDrawerWithGravity(ss.openDrawerGravity);\n                if (toOpen != null) openDrawer(toOpen);\n            }\n        } else super.onRestoreInstanceState(state);\n    }\n\n    @Override\n    protected Parcelable onSaveInstanceState() {\n        final Parcelable superState = super.onSaveInstanceState();\n        assert superState != null;\n        final SavedState ss = new SavedState(superState);\n\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = getChildAt(i);\n            final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n            // Is the current child fully opened (that is, not closing)?\n            final boolean isOpenedAndNotClosing = (lp.openState == LayoutParams.FLAG_IS_OPENED);\n            // Is the current child opening?\n            final boolean isClosedAndOpening = (lp.openState == LayoutParams.FLAG_IS_OPENING);\n            if (isOpenedAndNotClosing || isClosedAndOpening) {\n                // If one of the conditions above holds, save the child's gravity so that we open that child during state restore.\n                ss.openDrawerGravity = lp.gravity;\n                break;\n            }\n        }\n\n        return ss;\n    }\n\n    @Override\n    public void addView(final View child, final int index, final ViewGroup.LayoutParams params) {\n        super.addView(child, index, params);\n        final View openDrawer = findOpenDrawer();\n        if (openDrawer == null) isDrawerView(child);\n    }\n\n    protected static class SavedState extends AbsSavedState {\n        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {\n            @NonNull\n            @Override\n            public SavedState createFromParcel(final Parcel in, final ClassLoader loader) {\n                return new SavedState(in, loader);\n            }\n\n            @NonNull\n            @Override\n            public SavedState createFromParcel(final Parcel in) {\n                return new SavedState(in, null);\n            }\n\n            @NonNull\n            @Override\n            public SavedState[] newArray(final int size) {\n                return new SavedState[size];\n            }\n        };\n        int openDrawerGravity = Gravity.NO_GRAVITY;\n\n        public SavedState(@NonNull final Parcelable superState) {\n            super(superState);\n        }\n\n        public SavedState(@NonNull final Parcel in, @Nullable final ClassLoader loader) {\n            super(in, loader);\n            openDrawerGravity = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(final Parcel dest, final int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeInt(openDrawerGravity);\n        }\n    }\n\n    private class ViewDragCallback extends ViewDragHelper.Callback {\n        private final int mAbsGravity;\n        private ViewDragHelper mDragger;\n\n        ViewDragCallback(final int gravity) {\n            mAbsGravity = gravity;\n        }\n\n        public void setDragger(final ViewDragHelper dragger) {\n            mDragger = dragger;\n        }\n\n        @Override\n        public boolean tryCaptureView(@NonNull final View child, final int pointerId) {\n            return isDrawerView(child) && checkDrawerViewAbsoluteGravity(child, mAbsGravity);\n        }\n\n        @Override\n        public void onViewDragStateChanged(final int state) {\n            updateDrawerState(state, mDragger.getCapturedView());\n        }\n\n        @Override\n        public void onViewPositionChanged(@NonNull final View changedView, final int left, final int top, final int dx, final int dy) {\n            final float offset;\n            final int childWidth = changedView.getWidth();\n\n            if (checkDrawerViewAbsoluteGravity(changedView, Gravity.LEFT)) offset = (float) (childWidth + left) / childWidth;\n            else offset = (float) (getWidth() - left) / childWidth;\n\n            setDrawerViewOffset(changedView, offset);\n            changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);\n            invalidate();\n        }\n\n        @Override\n        public void onViewCaptured(@NonNull final View capturedChild, final int activePointerId) {\n            final LayoutParams lp = (LayoutParams) capturedChild.getLayoutParams();\n            lp.isPeeking = false;\n            closeOtherDrawer();\n        }\n\n        private void closeOtherDrawer() {\n            final int otherGrav = mAbsGravity == Gravity.LEFT ? Gravity.RIGHT : Gravity.LEFT;\n            final View toClose = findDrawerWithGravity(otherGrav);\n            if (toClose != null) closeDrawer(toClose);\n        }\n\n        @Override\n        public void onViewReleased(@NonNull final View releasedChild, final float xvel, final float yvel) {\n            final float offset = getDrawerViewOffset(releasedChild);\n            final int childWidth = releasedChild.getWidth();\n\n            final int left;\n            if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT))\n                left = xvel > 0 || (xvel == 0 && offset > 0.5f) ? 0 : -childWidth;\n            else {\n                final int width = getWidth();\n                left = xvel < 0 || (xvel == 0 && offset > 0.5f) ? width - childWidth : width;\n            }\n\n            mDragger.settleCapturedViewAt(left, releasedChild.getTop());\n            invalidate();\n        }\n\n        @Override\n        public void onEdgeDragStarted(final int edgeFlags, final int pointerId) {\n            final View toCapture;\n            if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT)\n                toCapture = findDrawerWithGravity(Gravity.LEFT);\n            else toCapture = findDrawerWithGravity(Gravity.RIGHT);\n\n            if (toCapture != null && isDrawerView(toCapture)) mDragger.captureChildView(toCapture, pointerId);\n        }\n\n        @Override\n        public int getViewHorizontalDragRange(@NonNull final View child) {\n            return isDrawerView(child) ? child.getWidth() : 0;\n        }\n\n        @Override\n        public int clampViewPositionHorizontal(@NonNull final View child, final int left, final int dx) {\n            if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) return Math.max(-child.getWidth(), Math.min(left, 0));\n            final int width = getWidth();\n            return Math.max(width - child.getWidth(), Math.min(left, width));\n        }\n\n        @Override\n        public int clampViewPositionVertical(@NonNull final View child, final int top, final int dy) {\n            return child.getTop();\n        }\n    }\n\n    public static class LayoutParams extends ViewGroup.MarginLayoutParams {\n        private static final int FLAG_IS_CLOSING = 0x4;\n        public static final int FLAG_IS_OPENED = 0x1;\n        public static final int FLAG_IS_OPENING = 0x2;\n        public int openState;\n        @EdgeGravity\n        public int gravity = Gravity.NO_GRAVITY;\n        public boolean isPeeking;\n        public float onScreen;\n\n        public LayoutParams(@NonNull final Context c, @Nullable final AttributeSet attrs) {\n            super(c, attrs);\n            final TypedArray a = c.obtainStyledAttributes(attrs, new int[]{android.R.attr.layout_gravity});\n            try {\n                this.gravity = a.getInt(0, Gravity.NO_GRAVITY);\n            } finally {\n                a.recycle();\n            }\n        }\n\n        public LayoutParams(final int width, final int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(@NonNull final LayoutParams source) {\n            super(source);\n            this.gravity = source.gravity;\n        }\n\n        public LayoutParams(@NonNull final ViewGroup.LayoutParams source) {\n            super(source);\n        }\n\n        public LayoutParams(@NonNull final ViewGroup.MarginLayoutParams source) {\n            super(source);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.lifecycle.LifecycleOwner;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.lifecycle.ViewModelStoreOwner;\nimport androidx.recyclerview.widget.LinearSmoothScroller;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.recyclerview.widget.StaggeredGridLayoutManager;\nimport androidx.transition.ChangeBounds;\nimport androidx.transition.Transition;\nimport androidx.transition.TransitionManager;\nimport androidx.work.Data;\nimport androidx.work.WorkInfo;\nimport androidx.work.WorkManager;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.MediaViewModel;\nimport awais.instagrabber.workers.DownloadWorker;\n\npublic class PostsRecyclerView extends RecyclerView {\n    private static final String TAG = \"PostsRecyclerView\";\n\n    private StaggeredGridLayoutManager layoutManager;\n    private PostsLayoutPreferences layoutPreferences;\n    private PostFetcher.PostFetchService postFetchService;\n    private Transition transition;\n    private ViewModelStoreOwner viewModelStoreOwner;\n    private FeedAdapterV2 feedAdapter;\n    private LifecycleOwner lifeCycleOwner;\n    private MediaViewModel mediaViewModel;\n    private boolean initCalled = false;\n    private GridSpacingItemDecoration gridSpacingItemDecoration;\n    private RecyclerLazyLoaderAtEdge lazyLoader;\n    private FeedAdapterV2.FeedItemCallback feedItemCallback;\n    private boolean shouldScrollToTop;\n    private FeedAdapterV2.SelectionModeCallback selectionModeCallback;\n\n    private final List<FetchStatusChangeListener> fetchStatusChangeListeners = new ArrayList<>();\n\n    private final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {\n        @Override\n        protected int getVerticalSnapPreference() {\n            return LinearSmoothScroller.SNAP_TO_START;\n        }\n    };\n\n    public PostsRecyclerView(@NonNull final Context context) {\n        super(context);\n    }\n\n    public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public PostsRecyclerView setViewModelStoreOwner(final ViewModelStoreOwner owner) {\n        if (initCalled) {\n            throw new IllegalArgumentException(\"init already called!\");\n        }\n        this.viewModelStoreOwner = owner;\n        return this;\n    }\n\n    public PostsRecyclerView setLifeCycleOwner(final LifecycleOwner lifeCycleOwner) {\n        if (initCalled) {\n            throw new IllegalArgumentException(\"init already called!\");\n        }\n        this.lifeCycleOwner = lifeCycleOwner;\n        return this;\n    }\n\n    public PostsRecyclerView setPostFetchService(final PostFetcher.PostFetchService postFetchService) {\n        if (initCalled) {\n            throw new IllegalArgumentException(\"init already called!\");\n        }\n        this.postFetchService = postFetchService;\n        return this;\n    }\n\n    public PostsRecyclerView setFeedItemCallback(@NonNull final FeedAdapterV2.FeedItemCallback feedItemCallback) {\n        this.feedItemCallback = feedItemCallback;\n        return this;\n    }\n\n    public PostsRecyclerView setSelectionModeCallback(@NonNull final FeedAdapterV2.SelectionModeCallback selectionModeCallback) {\n        this.selectionModeCallback = selectionModeCallback;\n        return this;\n    }\n\n    public PostsRecyclerView setLayoutPreferences(final PostsLayoutPreferences layoutPreferences) {\n        this.layoutPreferences = layoutPreferences;\n        if (initCalled) {\n            if (layoutPreferences == null) return this;\n            feedAdapter.setLayoutPreferences(layoutPreferences);\n            updateLayout();\n        }\n        return this;\n    }\n\n    public void init() {\n        initCalled = true;\n        if (viewModelStoreOwner == null) {\n            throw new IllegalArgumentException(\"ViewModelStoreOwner cannot be null\");\n        } else if (lifeCycleOwner == null) {\n            throw new IllegalArgumentException(\"LifecycleOwner cannot be null\");\n        } else if (postFetchService == null) {\n            throw new IllegalArgumentException(\"PostFetchService cannot be null\");\n        }\n        if (layoutPreferences == null) {\n            layoutPreferences = PostsLayoutPreferences.builder().build();\n            // Utils.settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, layoutPreferences.getJson());\n        }\n        gridSpacingItemDecoration = new GridSpacingItemDecoration(Utils.convertDpToPx(2));\n        initTransition();\n        initAdapter();\n        initLayoutManager();\n        initSelf();\n        initDownloadWorkerListener();\n    }\n\n    private void initTransition() {\n        transition = new ChangeBounds();\n        transition.setDuration(300);\n    }\n\n    private void initLayoutManager() {\n        layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);\n        setLayoutManager(layoutManager);\n    }\n\n    private void initAdapter() {\n        feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback, selectionModeCallback);\n        feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);\n        setAdapter(feedAdapter);\n    }\n\n    private void initSelf() {\n        try {\n            mediaViewModel = new ViewModelProvider(\n                    viewModelStoreOwner,\n                    new MediaViewModel.ViewModelFactory(postFetchService)\n            ).get(MediaViewModel.class);\n        } catch (Exception e) {\n            Log.e(TAG, \"initSelf: \", e);\n        }\n        if (mediaViewModel == null) return;\n        final LiveData<List<Media>> mediaListLiveData = mediaViewModel.getList();\n        mediaListLiveData.observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {\n            dispatchFetchStatus();\n            postDelayed(this::fetchMoreIfPossible, 1000);\n            if (!shouldScrollToTop) return;\n            shouldScrollToTop = false;\n            post(() -> smoothScrollToPosition(0));\n        }));\n        if (layoutPreferences.getHasGap()) {\n            addItemDecoration(gridSpacingItemDecoration);\n        }\n        setHasFixedSize(true);\n        setNestedScrollingEnabled(true);\n        setItemAnimator(null);\n        lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, (page) -> {\n            if (mediaViewModel.hasMore()) {\n                mediaViewModel.fetch();\n                dispatchFetchStatus();\n            }\n        });\n        addOnScrollListener(lazyLoader);\n        if (mediaListLiveData.getValue() == null || mediaListLiveData.getValue().isEmpty()) {\n            mediaViewModel.fetch();\n            dispatchFetchStatus();\n        }\n    }\n\n    private void fetchMoreIfPossible() {\n        if (!mediaViewModel.hasMore()) return;\n        if (feedAdapter.getItemCount() == 0) return;\n        final LayoutManager layoutManager = getLayoutManager();\n        if (!(layoutManager instanceof StaggeredGridLayoutManager)) return;\n        final int[] itemPositions = ((StaggeredGridLayoutManager) layoutManager).findLastCompletelyVisibleItemPositions(null);\n        final boolean allNoPosition = Arrays.stream(itemPositions).allMatch(position -> position == RecyclerView.NO_POSITION);\n        if (allNoPosition) return;\n        final boolean match = Arrays.stream(itemPositions).anyMatch(position -> position == feedAdapter.getItemCount() - 1);\n        if (!match) return;\n        mediaViewModel.fetch();\n        dispatchFetchStatus();\n    }\n\n    private void initDownloadWorkerListener() {\n        WorkManager.getInstance(getContext())\n                   .getWorkInfosByTagLiveData(\"download\")\n                   .observe(lifeCycleOwner, workInfoList -> {\n                       for (final WorkInfo workInfo : workInfoList) {\n                           if (workInfo == null) continue;\n                           final Data progress = workInfo.getProgress();\n                           final float progressPercent = progress.getFloat(DownloadWorker.PROGRESS, 0);\n                           if (progressPercent != 100) continue;\n                           final String url = progress.getString(DownloadWorker.URL);\n                           final List<Media> feedModels = mediaViewModel.getList().getValue();\n                           if (feedModels == null) continue;\n                           for (int i = 0; i < feedModels.size(); i++) {\n                               final Media feedModel = feedModels.get(i);\n                               final List<String> displayUrls = getDisplayUrl(feedModel);\n                               if (displayUrls.contains(url)) {\n                                   feedAdapter.notifyItemChanged(i);\n                                   break;\n                               }\n                           }\n                       }\n                   });\n    }\n\n    private List<String> getDisplayUrl(final Media feedModel) {\n        List<String> urls = Collections.emptyList();\n        if (feedModel == null || feedModel.getType() == null) return urls;\n        switch (feedModel.getType()) {\n            case MEDIA_TYPE_IMAGE:\n            case MEDIA_TYPE_VIDEO:\n                urls = Collections.singletonList(ResponseBodyUtils.getImageUrl(feedModel));\n                break;\n            case MEDIA_TYPE_SLIDER:\n                final List<Media> sliderItems = feedModel.getCarouselMedia();\n                if (sliderItems != null) {\n                    final ImmutableList.Builder<String> builder = ImmutableList.builder();\n                    for (final Media child : sliderItems) {\n                        builder.add(ResponseBodyUtils.getImageUrl(child));\n                    }\n                    urls = builder.build();\n                }\n                break;\n            default:\n        }\n        return urls;\n    }\n\n    private void updateLayout() {\n        post(() -> {\n            TransitionManager.beginDelayedTransition(this, transition);\n            feedAdapter.notifyDataSetChanged();\n            final int itemDecorationCount = getItemDecorationCount();\n            if (!layoutPreferences.getHasGap()) {\n                if (itemDecorationCount == 1) {\n                    removeItemDecoration(gridSpacingItemDecoration);\n                }\n            } else {\n                if (itemDecorationCount == 0) {\n                    addItemDecoration(gridSpacingItemDecoration);\n                }\n            }\n            if (layoutPreferences.getType() == PostsLayoutPreferences.PostsLayoutType.LINEAR) {\n                if (layoutManager.getSpanCount() != 1) {\n                    layoutManager.setSpanCount(1);\n                    setAdapter(null);\n                    setAdapter(feedAdapter);\n                }\n            } else {\n                boolean shouldRedraw = layoutManager.getSpanCount() == 1;\n                layoutManager.setSpanCount(layoutPreferences.getColCount());\n                if (shouldRedraw) {\n                    setAdapter(null);\n                    setAdapter(feedAdapter);\n                }\n            }\n        });\n    }\n\n    public void refresh() {\n        shouldScrollToTop = true;\n        if (lazyLoader != null) {\n            lazyLoader.resetState();\n        }\n        if (mediaViewModel != null) {\n            mediaViewModel.refresh();\n        }\n        dispatchFetchStatus();\n    }\n\n    public boolean isFetching() {\n        return mediaViewModel != null && mediaViewModel.isFetching();\n    }\n\n    public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) {\n        if (fetchStatusChangeListener == null) return this;\n        fetchStatusChangeListeners.add(fetchStatusChangeListener);\n        return this;\n    }\n\n    public void removeFetchStatusListener(final FetchStatusChangeListener fetchStatusChangeListener) {\n        if (fetchStatusChangeListener == null) return;\n        fetchStatusChangeListeners.remove(fetchStatusChangeListener);\n    }\n\n    private void dispatchFetchStatus() {\n        for (final FetchStatusChangeListener listener : fetchStatusChangeListeners) {\n            listener.onFetchStatusChange(isFetching());\n        }\n    }\n\n    public PostsLayoutPreferences getLayoutPreferences() {\n        return layoutPreferences;\n    }\n\n    public void endSelection() {\n        feedAdapter.endSelection();\n    }\n\n    public interface FetchStatusChangeListener {\n        void onFetchStatusChange(boolean fetching);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        lifeCycleOwner = null;\n        initCalled = false;\n    }\n\n    @Override\n    public void smoothScrollToPosition(final int position) {\n        smoothScroller.setTargetPosition(position);\n        layoutManager.startSmoothScroll(smoothScroller);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/PrimaryActionModeCallback.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.view.ActionMode;\nimport android.view.Menu;\nimport android.view.MenuItem;\n\npublic class PrimaryActionModeCallback implements ActionMode.Callback {\n    private ActionMode mode;\n    private int menuRes;\n    private final Callbacks callbacks;\n\n    public PrimaryActionModeCallback(final int menuRes, final Callbacks callbacks) {\n        this.menuRes = menuRes;\n        this.callbacks = callbacks;\n    }\n\n    @Override\n    public boolean onCreateActionMode(final ActionMode mode, final Menu menu) {\n        this.mode = mode;\n        mode.getMenuInflater().inflate(menuRes, menu);\n        if (callbacks != null) {\n            callbacks.onCreate(mode, menu);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {\n        return false;\n    }\n\n    @Override\n    public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n        if (callbacks != null) {\n            return callbacks.onActionItemClicked(mode, item);\n        }\n        return false;\n    }\n\n    @Override\n    public void onDestroyActionMode(final ActionMode mode) {\n        if (callbacks != null) {\n            callbacks.onDestroy(mode);\n        }\n        this.mode = null;\n    }\n\n    public abstract static class CallbacksHelper implements Callbacks {\n        public void onCreate(final ActionMode mode, final Menu menu) {\n\n        }\n\n        @Override\n        public void onDestroy(final ActionMode mode) {\n\n        }\n\n        @Override\n        public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n            return false;\n        }\n    }\n\n    public interface Callbacks {\n        void onCreate(final ActionMode mode, final Menu menu);\n\n        void onDestroy(final ActionMode mode);\n\n        boolean onActionItemClicked(final ActionMode mode, final MenuItem item);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/ProfilePicView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.DimenRes;\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\nimport com.facebook.drawee.generic.RoundingParams;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\n\npublic final class ProfilePicView extends CircularImageView {\n    private static final String TAG = \"ProfilePicView\";\n\n    private Size size;\n    private int dimensionPixelSize;\n\n    public ProfilePicView(Context context, GenericDraweeHierarchy hierarchy) {\n        super(context);\n        setHierarchy(hierarchy);\n        size = Size.REGULAR;\n        updateLayout();\n    }\n\n    public ProfilePicView(final Context context) {\n        super(context);\n        size = Size.REGULAR;\n        updateLayout();\n    }\n\n    public ProfilePicView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n        parseAttrs(context, attrs);\n        updateLayout();\n    }\n\n    public ProfilePicView(final Context context,\n                          final AttributeSet attrs,\n                          final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        parseAttrs(context, attrs);\n        updateLayout();\n    }\n\n    private void parseAttrs(final Context context, final AttributeSet attrs) {\n        final TypedArray a = context.getTheme().obtainStyledAttributes(\n                attrs,\n                R.styleable.ProfilePicView,\n                0,\n                0);\n        try {\n            final int sizeValue = a.getInt(R.styleable.ProfilePicView_size, Size.REGULAR.getValue());\n            size = Size.valueOf(sizeValue);\n        } finally {\n            a.recycle();\n        }\n    }\n\n    private void updateLayout() {\n        @DimenRes final int dimenRes;\n        switch (size) {\n            case SMALL:\n                dimenRes = R.dimen.profile_pic_size_small;\n                break;\n            case SMALLER:\n                dimenRes = R.dimen.profile_pic_size_smaller;\n                break;\n            case TINY:\n                dimenRes = R.dimen.profile_pic_size_tiny;\n                break;\n            case LARGE:\n                dimenRes = R.dimen.profile_pic_size_large;\n                break;\n            default:\n            case REGULAR:\n                dimenRes = R.dimen.profile_pic_size_regular;\n                break;\n        }\n        ViewGroup.LayoutParams layoutParams = getLayoutParams();\n        if (layoutParams == null) {\n            layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        }\n        dimensionPixelSize = getResources().getDimensionPixelSize(dimenRes);\n        layoutParams.width = dimensionPixelSize;\n        layoutParams.height = dimensionPixelSize;\n\n        // invalidate();\n        // requestLayout();\n    }\n\n    public void setSize(final Size size) {\n        this.size = size;\n        updateLayout();\n    }\n\n    public void setStoriesBorder() {\n        // private final int borderSize = 8;\n        final int color = Color.GREEN;\n        RoundingParams roundingParams = getHierarchy().getRoundingParams();\n        if (roundingParams == null) {\n            roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);\n        }\n        roundingParams.setBorder(color, 5.0f);\n        getHierarchy().setRoundingParams(roundingParams);\n    }\n\n    public enum Size {\n        TINY(0),\n        SMALL(1),\n        REGULAR(2),\n        LARGE(3),\n        SMALLER(4);\n\n        private final int value;\n        private static final Map<Integer, Size> map = new HashMap<>();\n\n        static {\n            for (Size size : Size.values()) {\n                map.put(size.value, size);\n            }\n        }\n\n        Size(final int value) {\n            this.value = value;\n        }\n\n        @NonNull\n        public static Size valueOf(final int value) {\n            final Size size = map.get(value);\n            return size != null ? size : Size.REGULAR;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n\n    @Override\n    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        setMeasuredDimension(dimensionPixelSize, dimensionPixelSize);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.text.InputFilter;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.emoji.widget.EmojiTextViewHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.github.armcha.autolink.AutoLinkItem;\nimport io.github.armcha.autolink.AutoLinkTextView;\nimport io.github.armcha.autolink.MODE_EMAIL;\nimport io.github.armcha.autolink.MODE_HASHTAG;\nimport io.github.armcha.autolink.MODE_MENTION;\nimport io.github.armcha.autolink.MODE_URL;\nimport io.github.armcha.autolink.Mode;\n\npublic class RamboTextViewV2 extends AutoLinkTextView {\n    private final List<OnMentionClickListener> onMentionClickListeners = new ArrayList<>();\n    private final List<OnHashtagClickListener> onHashtagClickListeners = new ArrayList<>();\n    private final List<OnURLClickListener> onURLClickListeners = new ArrayList<>();\n    private final List<OnEmailClickListener> onEmailClickListeners = new ArrayList<>();\n\n    private EmojiTextViewHelper emojiTextViewHelper;\n\n    public RamboTextViewV2(@NonNull final Context context,\n                           @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    private void init() {\n        getEmojiTextViewHelper().updateTransformationMethod();\n        addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE, MODE_URL.INSTANCE);\n        onAutoLinkClick(autoLinkItem -> {\n            final Mode mode = autoLinkItem.getMode();\n            if (mode.equals(MODE_MENTION.INSTANCE)) {\n                for (final OnMentionClickListener onMentionClickListener : onMentionClickListeners) {\n                    onMentionClickListener.onMentionClick(autoLinkItem);\n                }\n                return;\n            }\n            if (mode.equals(MODE_HASHTAG.INSTANCE)) {\n                for (final OnHashtagClickListener onHashtagClickListener : onHashtagClickListeners) {\n                    onHashtagClickListener.onHashtagClick(autoLinkItem);\n                }\n                return;\n            }\n            if (mode.equals(MODE_URL.INSTANCE)) {\n                for (final OnURLClickListener onURLClickListener : onURLClickListeners) {\n                    onURLClickListener.onURLClick(autoLinkItem);\n                }\n                return;\n            }\n            if (mode.equals(MODE_EMAIL.INSTANCE)) {\n                for (final OnEmailClickListener onEmailClickListener : onEmailClickListeners) {\n                    onEmailClickListener.onEmailClick(autoLinkItem);\n                }\n            }\n        });\n        onAutoLinkLongClick(autoLinkItem -> {});\n    }\n\n    @Override\n    public void setFilters(InputFilter[] filters) {\n        super.setFilters(getEmojiTextViewHelper().getFilters(filters));\n    }\n\n    @Override\n    public void setAllCaps(boolean allCaps) {\n        super.setAllCaps(allCaps);\n        getEmojiTextViewHelper().setAllCaps(allCaps);\n    }\n\n\n    private EmojiTextViewHelper getEmojiTextViewHelper() {\n        if (emojiTextViewHelper == null) {\n            emojiTextViewHelper = new EmojiTextViewHelper(this);\n        }\n        return emojiTextViewHelper;\n    }\n\n    public void addOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {\n        if (onMentionClickListener == null) {\n            return;\n        }\n        onMentionClickListeners.add(onMentionClickListener);\n    }\n\n    public void removeOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {\n        if (onMentionClickListener == null) {\n            return;\n        }\n        onMentionClickListeners.remove(onMentionClickListener);\n    }\n\n    public void clearOnMentionClickListeners() {\n        onMentionClickListeners.clear();\n    }\n\n    public void addOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {\n        if (onHashtagClickListener == null) {\n            return;\n        }\n        onHashtagClickListeners.add(onHashtagClickListener);\n    }\n\n    public void removeOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {\n        if (onHashtagClickListener == null) {\n            return;\n        }\n        onHashtagClickListeners.remove(onHashtagClickListener);\n    }\n\n    public void clearOnHashtagClickListeners() {\n        onHashtagClickListeners.clear();\n    }\n\n    public void addOnURLClickListener(final OnURLClickListener onURLClickListener) {\n        if (onURLClickListener == null) {\n            return;\n        }\n        onURLClickListeners.add(onURLClickListener);\n    }\n\n    public void removeOnURLClickListener(final OnURLClickListener onURLClickListener) {\n        if (onURLClickListener == null) {\n            return;\n        }\n        onURLClickListeners.remove(onURLClickListener);\n    }\n\n    public void clearOnURLClickListeners() {\n        onURLClickListeners.clear();\n    }\n\n    public void addOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {\n        if (onEmailClickListener == null) {\n            return;\n        }\n        onEmailClickListeners.add(onEmailClickListener);\n    }\n\n    public void removeOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {\n        if (onEmailClickListener == null) {\n            return;\n        }\n        onEmailClickListeners.remove(onEmailClickListener);\n    }\n\n    public void clearOnEmailClickListeners() {\n        onEmailClickListeners.clear();\n    }\n\n    public void clearAllAutoLinkListeners() {\n        clearOnMentionClickListeners();\n        clearOnHashtagClickListeners();\n        clearOnURLClickListeners();\n        clearOnEmailClickListeners();\n    }\n\n    public interface OnMentionClickListener {\n        void onMentionClick(final AutoLinkItem autoLinkItem);\n    }\n\n    public interface OnHashtagClickListener {\n        void onHashtagClick(final AutoLinkItem autoLinkItem);\n    }\n\n    public interface OnURLClickListener {\n        void onURLClick(final AutoLinkItem autoLinkItem);\n    }\n\n    public interface OnEmailClickListener {\n        void onEmailClick(final AutoLinkItem autoLinkItem);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/ReactionEmojiTextView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.text.SpannableString;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.emoji.widget.EmojiAppCompatTextView;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class ReactionEmojiTextView extends EmojiAppCompatTextView {\n    private static final String TAG = ReactionEmojiTextView.class.getSimpleName();\n\n    private final SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();\n\n    private String count = \"\";\n    private SpannableString ellipsisSpannable;\n    private String distinctEmojis;\n\n    public ReactionEmojiTextView(final Context context) {\n        super(context);\n        init();\n    }\n\n    public ReactionEmojiTextView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ReactionEmojiTextView(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        ellipsisSpannable = new SpannableString(count);\n    }\n\n    @SuppressLint(\"SetTextI18n\")\n    public void setEmojis(@NonNull final List<String> emojis) {\n        count = String.valueOf(emojis.size());\n        distinctEmojis = emojis.stream()\n                               .distinct()\n                               .collect(Collectors.joining());\n        ellipsisSpannable = new SpannableString(count);\n        setText(distinctEmojis + (emojis.size() > 1 ? count : \"\"));\n    }\n\n    @Override\n    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        final CharSequence text = getText();\n        if (text == null) return;\n        final int measuredWidth = getMeasuredWidth();\n        float availableTextWidth = measuredWidth - getCompoundPaddingLeft() - getCompoundPaddingRight();\n        CharSequence ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize());\n        if (!ellipsizedText.toString().equals(text.toString())) {\n            // If the ellipsizedText is different than the original text, this means that it didn't fit and got indeed ellipsized.\n            // Calculate the new availableTextWidth by taking into consideration the size of the custom ellipsis, too.\n            availableTextWidth = (availableTextWidth - getPaint().measureText(count));\n            ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize());\n            final int defaultEllipsisStart = ellipsizedText.toString().indexOf(getDefaultEllipsis());\n            final int defaultEllipsisEnd = defaultEllipsisStart + 1;\n            spannableStringBuilder.clear();\n            // Update the text with the ellipsized version and replace the default ellipsis with the custom one.\n            final SpannableStringBuilder replace = spannableStringBuilder.append(ellipsizedText)\n                                                                         .replace(defaultEllipsisStart, defaultEllipsisEnd, ellipsisSpannable);\n            setText(replace);\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        }\n    }\n\n    private char getDefaultEllipsis() {\n        return '…';\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/RecordButton.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport com.google.android.material.button.MaterialButton;\n\nimport awais.instagrabber.animations.ScaleAnimation;\n\n/**\n * Created by Devlomi on 13/12/2017.\n */\n\npublic class RecordButton extends MaterialButton implements View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {\n\n    private ScaleAnimation scaleAnimation;\n    private RecordView recordView;\n    private boolean listenForRecord = true;\n    private OnRecordClickListener onRecordClickListener;\n    private OnRecordLongClickListener onRecordLongClickListener;\n\n    public RecordButton(Context context) {\n        super(context);\n        init(context, null);\n    }\n\n    public RecordButton(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs);\n    }\n\n    public RecordButton(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs);\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    private void init(Context context, AttributeSet attrs) {\n        scaleAnimation = new ScaleAnimation(this);\n        this.setOnTouchListener(this);\n        this.setOnClickListener(this);\n        this.setOnLongClickListener(this);\n    }\n\n    public void setRecordView(RecordView recordView) {\n        this.recordView = recordView;\n    }\n\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        if (isListenForRecord()) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    recordView.onActionDown((RecordButton) v, event);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    recordView.onActionMove((RecordButton) v, event, false);\n                    break;\n                case MotionEvent.ACTION_UP:\n                    recordView.onActionUp((RecordButton) v);\n                    break;\n            }\n        }\n        return isListenForRecord();\n    }\n\n    protected void startScale() {\n        scaleAnimation.start();\n    }\n\n    public void stopScale() {\n        scaleAnimation.stop();\n    }\n\n    public void setListenForRecord(boolean listenForRecord) {\n        this.listenForRecord = listenForRecord;\n    }\n\n    public boolean isListenForRecord() {\n        return listenForRecord;\n    }\n\n    public void setOnRecordClickListener(OnRecordClickListener onRecordClickListener) {\n        this.onRecordClickListener = onRecordClickListener;\n    }\n\n    public void setOnRecordLongClickListener(OnRecordLongClickListener onRecordLongClickListener) {\n        this.onRecordLongClickListener = onRecordLongClickListener;\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (onRecordClickListener != null) {\n            onRecordClickListener.onClick(v);\n        }\n    }\n\n    @Override\n    public boolean onLongClick(final View v) {\n        if (onRecordLongClickListener != null) {\n            return onRecordLongClickListener.onLongClick(v);\n        }\n        return false;\n    }\n\n    public interface OnRecordClickListener {\n        void onClick(View v);\n    }\n\n    public interface OnRecordLongClickListener {\n        boolean onLongClick(View v);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/RecordView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.media.MediaPlayer;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.widget.RelativeLayout;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.graphics.drawable.DrawableCompat;\n\nimport java.io.IOException;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.customviews.helpers.RecordViewAnimationHelper;\nimport awais.instagrabber.databinding.RecordViewLayoutBinding;\nimport awais.instagrabber.utils.Utils;\n\n/**\n * Created by Devlomi on 24/08/2017.\n */\n\npublic class RecordView extends RelativeLayout {\n    private static final String TAG = RecordView.class.getSimpleName();\n\n    public static final int DEFAULT_CANCEL_BOUNDS = 8; //8dp\n    // private ImageView smallBlinkingMic;\n    // private ImageView basketImg;\n    // private Chronometer counterTime;\n    // private TextView slideToCancel;\n    // private LinearLayout slideToCancelLayout;\n    private float initialX;\n    private float basketInitialY;\n    private float difX = 0;\n    private float cancelBounds = DEFAULT_CANCEL_BOUNDS;\n    private long startTime;\n    private final Context context;\n    private OnRecordListener onRecordListener;\n    private boolean isSwiped;\n    private boolean isLessThanMinAllowed = false;\n    private boolean isSoundEnabled = true;\n    private int RECORD_START = R.raw.record_start;\n    private int RECORD_FINISHED = R.raw.record_finished;\n    private int RECORD_ERROR = R.raw.record_error;\n    private RecordViewAnimationHelper recordViewAnimationHelper;\n    private RecordViewLayoutBinding binding;\n    private int minMillis = 1000;\n\n\n    public RecordView(Context context) {\n        super(context);\n        this.context = context;\n        init(context, null, -1, -1);\n    }\n\n\n    public RecordView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        this.context = context;\n        init(context, attrs, -1, -1);\n    }\n\n    public RecordView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        this.context = context;\n        init(context, attrs, defStyleAttr, -1);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        binding = RecordViewLayoutBinding.inflate(LayoutInflater.from(context), this, false);\n        addView(binding.getRoot());\n        hideViews(true);\n        if (attrs != null && defStyleAttr == -1 && defStyleRes == -1) {\n            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordView, defStyleAttr, defStyleRes);\n            int slideArrowResource = typedArray.getResourceId(R.styleable.RecordView_slide_to_cancel_arrow, -1);\n            String slideToCancelText = typedArray.getString(R.styleable.RecordView_slide_to_cancel_text);\n            int slideToCancelTextColor = typedArray.getResourceId(R.styleable.RecordView_slide_to_cancel_text_color, -1);\n            int slideMarginRight = (int) typedArray.getDimension(R.styleable.RecordView_slide_to_cancel_margin_right, 30);\n            int counterTimeColor = typedArray.getResourceId(R.styleable.RecordView_counter_time_color, -1);\n            int arrowColor = typedArray.getResourceId(R.styleable.RecordView_slide_to_cancel_arrow_color, -1);\n            int cancelBounds = typedArray.getDimensionPixelSize(R.styleable.RecordView_slide_to_cancel_bounds, -1);\n            if (cancelBounds != -1) {\n                setCancelBounds(cancelBounds, false);//don't convert it to pixels since it's already in pixels\n            }\n            if (slideToCancelText != null) {\n                setSlideToCancelText(slideToCancelText);\n            }\n            if (slideToCancelTextColor != -1) {\n                setSlideToCancelTextColor(getResources().getColor(slideToCancelTextColor));\n            }\n            if (slideArrowResource != -1) {\n                setSlideArrowDrawable(slideArrowResource);\n            }\n            if (arrowColor != -1) {\n                setSlideToCancelArrowColor(getResources().getColor(arrowColor));\n            }\n            if (counterTimeColor != -1) {\n                setCounterTimeColor(getResources().getColor(counterTimeColor));\n            }\n            setMarginRight(slideMarginRight, true);\n            typedArray.recycle();\n        }\n        recordViewAnimationHelper = new RecordViewAnimationHelper(context, binding.basketImg, binding.glowingMic);\n    }\n\n    private void hideViews(boolean hideSmallMic) {\n        binding.slideToCancel.setVisibility(GONE);\n        binding.basketImg.setVisibility(GONE);\n        binding.counterTv.setVisibility(GONE);\n        if (hideSmallMic) {\n            binding.glowingMic.setVisibility(GONE);\n        }\n    }\n\n    private void showViews() {\n        binding.slideToCancel.setVisibility(VISIBLE);\n        binding.glowingMic.setVisibility(VISIBLE);\n        binding.counterTv.setVisibility(VISIBLE);\n    }\n\n    private boolean isLessThanMin(long time) {\n        return time <= minMillis;\n    }\n\n    private void playSound(int soundRes) {\n        if (!isSoundEnabled) return;\n        if (soundRes == 0) return;\n        try {\n            final MediaPlayer player = new MediaPlayer();\n            AssetFileDescriptor afd = context.getResources().openRawResourceFd(soundRes);\n            if (afd == null) return;\n            player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());\n            afd.close();\n            player.prepare();\n            player.start();\n            player.setOnCompletionListener(MediaPlayer::release);\n            player.setLooping(false);\n        } catch (IOException e) {\n            Log.e(TAG, \"playSound\", e);\n        }\n    }\n\n    protected void onActionDown(RecordButton recordBtn, MotionEvent motionEvent) {\n        if (onRecordListener != null) {\n            onRecordListener.onStart();\n        }\n        recordViewAnimationHelper.setStartRecorded(true);\n        recordViewAnimationHelper.resetBasketAnimation();\n        recordViewAnimationHelper.resetSmallMic();\n        recordBtn.startScale();\n        // slideToCancelLayout.startShimmerAnimation();\n\n        initialX = recordBtn.getX();\n        basketInitialY = binding.basketImg.getY() + 90;\n        // playSound(RECORD_START);\n        showViews();\n\n        recordViewAnimationHelper.animateSmallMicAlpha();\n        binding.counterTv.setBase(SystemClock.elapsedRealtime());\n        startTime = System.currentTimeMillis();\n        binding.counterTv.start();\n        isSwiped = false;\n    }\n\n    protected void onActionMove(RecordButton recordBtn, MotionEvent motionEvent, final boolean forceCancel) {\n        long time = System.currentTimeMillis() - startTime;\n        if (isSwiped) return;\n        //Swipe To Cancel\n        if (forceCancel || (binding.slideToCancel.getX() != 0 && binding.slideToCancel.getX() <= binding.counterTv.getRight() + cancelBounds)) {\n            //if the time was less than one second then do not start basket animation\n            if (isLessThanMin(time)) {\n                hideViews(true);\n                recordViewAnimationHelper.clearAlphaAnimation(false);\n                if (onRecordListener != null) {\n                    onRecordListener.onLessThanMin();\n                }\n                recordViewAnimationHelper.onAnimationEnd();\n            } else {\n                hideViews(false);\n                recordViewAnimationHelper.animateBasket(basketInitialY);\n            }\n            recordViewAnimationHelper.moveRecordButtonAndSlideToCancelBack(recordBtn, binding.slideToCancel, initialX, difX);\n            binding.counterTv.stop();\n            // slideToCancelLayout.stopShimmerAnimation();\n            isSwiped = true;\n            recordViewAnimationHelper.setStartRecorded(false);\n            if (onRecordListener != null) {\n                onRecordListener.onCancel();\n            }\n            return;\n        }\n        //if statement is to Prevent Swiping out of bounds\n        if (!(motionEvent.getRawX() < initialX)) return;\n        recordBtn.animate()\n                 .x(motionEvent.getRawX())\n                 .setDuration(0)\n                 .start();\n        if (difX == 0) {\n            difX = (initialX - binding.slideToCancel.getX());\n        }\n        binding.slideToCancel.animate()\n                             .x(motionEvent.getRawX() - difX)\n                             .setDuration(0)\n                             .start();\n    }\n\n    protected void onActionUp(RecordButton recordBtn) {\n        final long elapsedTime = System.currentTimeMillis() - startTime;\n        if (!isLessThanMinAllowed && isLessThanMin(elapsedTime) && !isSwiped) {\n            if (onRecordListener != null) {\n                onRecordListener.onLessThanMin();\n            }\n            recordViewAnimationHelper.setStartRecorded(false);\n            // playSound(RECORD_ERROR);\n        } else {\n            if (onRecordListener != null && !isSwiped) {\n                onRecordListener.onFinish(elapsedTime);\n            }\n            recordViewAnimationHelper.setStartRecorded(false);\n            if (!isSwiped) {\n                // playSound(RECORD_FINISHED);\n            }\n        }\n        //if user has swiped then do not hide SmallMic since it will be hidden after swipe Animation\n        hideViews(!isSwiped);\n        if (!isSwiped) {\n            recordViewAnimationHelper.clearAlphaAnimation(true);\n        }\n        recordViewAnimationHelper.moveRecordButtonAndSlideToCancelBack(recordBtn, binding.slideToCancel, initialX, difX);\n        binding.counterTv.stop();\n        // slideToCancelLayout.stopShimmerAnimation();\n    }\n\n    private void setMarginRight(int marginRight, boolean convertToDp) {\n        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.slideToCancel.getLayoutParams();\n        if (convertToDp) {\n            layoutParams.rightMargin = Utils.convertDpToPx(marginRight);\n        } else {\n            layoutParams.rightMargin = marginRight;\n        }\n        binding.slideToCancel.setLayoutParams(layoutParams);\n    }\n\n    private void setSlideArrowDrawable(@DrawableRes final int slideArrowResource) {\n        Drawable slideArrow = AppCompatResources.getDrawable(getContext(), slideArrowResource);\n        // Log.d(TAG, \"setSlideArrowDrawable: slideArrow: \" + slideArrow);\n        if (slideArrow == null) return;\n        slideArrow.setBounds(0, 0, slideArrow.getIntrinsicWidth(), slideArrow.getIntrinsicHeight());\n        binding.slideToCancel.setCompoundDrawablesRelative(slideArrow, null, null, null);\n    }\n\n    public void setOnRecordListener(OnRecordListener onRecordListener) {\n        this.onRecordListener = onRecordListener;\n    }\n\n    public void setOnBasketAnimationEndListener(OnBasketAnimationEnd onBasketAnimationEndListener) {\n        recordViewAnimationHelper.setOnBasketAnimationEndListener(onBasketAnimationEndListener);\n    }\n\n    public void setSoundEnabled(boolean isEnabled) {\n        isSoundEnabled = isEnabled;\n    }\n\n    public void setLessThanMinAllowed(boolean isAllowed) {\n        isLessThanMinAllowed = isAllowed;\n    }\n\n    public void setSlideToCancelText(String text) {\n        binding.slideToCancel.setText(text);\n    }\n\n    public void setSlideToCancelTextColor(int color) {\n        binding.slideToCancel.setTextColor(color);\n    }\n\n    public void setSmallMicColor(int color) {\n        binding.glowingMic.setColorFilter(color);\n    }\n\n    public void setSmallMicIcon(int icon) {\n        binding.glowingMic.setImageResource(icon);\n    }\n\n    public void setSlideMarginRight(int marginRight) {\n        setMarginRight(marginRight, true);\n    }\n\n    public void setCustomSounds(int startSound, int finishedSound, int errorSound) {\n        //0 means do not play sound\n        RECORD_START = startSound;\n        RECORD_FINISHED = finishedSound;\n        RECORD_ERROR = errorSound;\n    }\n\n    public float getCancelBounds() {\n        return cancelBounds;\n    }\n\n    public void setCancelBounds(float cancelBounds) {\n        setCancelBounds(cancelBounds, true);\n    }\n\n    //set Chronometer color\n    public void setCounterTimeColor(@ColorInt int color) {\n        binding.counterTv.setTextColor(color);\n    }\n\n    public void setSlideToCancelArrowColor(@ColorInt int color) {\n        Drawable drawable = binding.slideToCancel.getCompoundDrawablesRelative()[0];\n        drawable = DrawableCompat.wrap(drawable);\n        DrawableCompat.setTint(drawable.mutate(), color);\n        binding.slideToCancel.setCompoundDrawablesRelative(drawable, null, null, null);\n    }\n\n    private void setCancelBounds(float cancelBounds, boolean convertDpToPixel) {\n        this.cancelBounds = convertDpToPixel ? Utils.convertDpToPx(cancelBounds) : cancelBounds;\n    }\n\n    public void setMinMillis(final int minMillis) {\n        this.minMillis = minMillis;\n    }\n\n    public void cancelRecording(final RecordButton recordBtn) {\n        onActionMove(recordBtn, null, true);\n    }\n\n    public interface OnBasketAnimationEnd {\n        void onAnimationEnd();\n    }\n\n    public interface OnRecordListener {\n        void onStart();\n\n        void onCancel();\n\n        void onFinish(long recordTime);\n\n        void onLessThanMin();\n    }\n}\n\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.animation.Animator;\nimport android.graphics.Rect;\nimport android.os.Handler;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\nimport android.view.animation.AccelerateDecelerateInterpolator;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.transition.ChangeBounds;\nimport androidx.transition.ChangeTransform;\nimport androidx.transition.Transition;\nimport androidx.transition.TransitionListenerAdapter;\nimport androidx.transition.TransitionManager;\nimport androidx.transition.TransitionSet;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport awais.instagrabber.utils.Utils;\n\npublic abstract class SharedElementTransitionDialogFragment extends DialogFragment {\n    // private static final String TAG = \"SETDialogFragment\";\n    private static final int DURATION = 200;\n\n    private final Map<Integer, View> startViews = new HashMap<>();\n    private final Map<Integer, View> destViews = new HashMap<>();\n    private final Map<Integer, ViewBounds> viewBoundsMap = new HashMap<>();\n    private final List<Animator> additionalAnimators = new ArrayList<>();\n    private final Handler initialBoundsHandler = new Handler();\n\n    private boolean startCalled = false;\n    private boolean startInitiated = false;\n    private int boundsCalculatedCount = 0;\n\n    protected int getAnimationDuration() {\n        return DURATION;\n    }\n\n    public void addSharedElement(@NonNull final View startView, @NonNull final View destView) {\n        final int key = destView.hashCode();\n        startViews.put(key, startView);\n        destViews.put(key, destView);\n        setupInitialBounds(startView, destView);\n        // final View view = getView();\n        // if (view == null) return;\n        // view.post(() -> {});\n    }\n\n    public void startPostponedEnterTransition() {\n        startCalled = true;\n        if (startInitiated) return;\n        if (boundsCalculatedCount < startViews.size()) return;\n        startInitiated = true;\n        final Set<Integer> keySet = startViews.keySet();\n        final View view = getView();\n        if (!(view instanceof ViewGroup)) return;\n        final TransitionSet transitionSet = new TransitionSet()\n                .setDuration(DURATION)\n                .setInterpolator(new AccelerateDecelerateInterpolator())\n                .addTransition(new ChangeBounds())\n                .addTransition(new ChangeTransform())\n                .addListener(new TransitionListenerAdapter() {\n                    @Override\n                    public void onTransitionStart(@NonNull final Transition transition) {\n                        for (Animator animator : additionalAnimators) {\n                            animator.start();\n                        }\n                    }\n\n                    @Override\n                    public void onTransitionEnd(@NonNull final Transition transition) {\n                        for (final Integer key : keySet) {\n                            final View startView = startViews.get(key);\n                            final View destView = destViews.get(key);\n                            final ViewBounds viewBounds = viewBoundsMap.get(key);\n                            if (startView == null || destView == null || viewBounds == null) return;\n                            onEndSharedElementAnimation(startView, destView, viewBounds);\n                        }\n                    }\n                });\n        view.post(() -> {\n            TransitionManager.beginDelayedTransition((ViewGroup) view, transitionSet);\n            for (final Integer key : keySet) {\n                final View startView = startViews.get(key);\n                final View destView = destViews.get(key);\n                final ViewBounds viewBounds = viewBoundsMap.get(key);\n                if (startView == null || destView == null || viewBounds == null) return;\n                onBeforeSharedElementAnimation(startView, destView, viewBounds);\n                setDestBounds(key);\n            }\n        });\n    }\n\n    private void setDestBounds(final int key) {\n        final View startView = startViews.get(key);\n        if (startView == null) return;\n        final View destView = destViews.get(key);\n        if (destView == null) return;\n        final ViewBounds viewBounds = viewBoundsMap.get(key);\n        if (viewBounds == null) return;\n        destView.setX((int) viewBounds.getDestX());\n        destView.setY((int) viewBounds.getDestY());\n        destView.setTranslationX(0);\n        destView.setTranslationY(0);\n        final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();\n        layoutParams.height = viewBounds.getDestHeight();\n        layoutParams.width = viewBounds.getDestWidth();\n        destView.requestLayout();\n    }\n\n    protected void onBeforeSharedElementAnimation(@NonNull final View startView,\n                                                  @NonNull final View destView,\n                                                  @NonNull final ViewBounds viewBounds) {}\n\n    protected void onEndSharedElementAnimation(@NonNull final View startView,\n                                               @NonNull final View destView,\n                                               @NonNull final ViewBounds viewBounds) {}\n\n    private void setupInitialBounds(@NonNull final View startView, @NonNull final View destView) {\n        final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {\n            private boolean firstPassDone;\n\n            @Override\n            public boolean onPreDraw() {\n                destView.getViewTreeObserver().removeOnPreDrawListener(this);\n                if (!firstPassDone) {\n                    getViewBounds(startView, destView, this);\n                    firstPassDone = true;\n                    return false;\n                }\n                final int[] location = new int[2];\n                startView.getLocationOnScreen(location);\n                final int initX = location[0];\n                final int initY = location[1];\n                destView.setX(initX);\n                destView.setY(initY - Utils.getStatusBarHeight(getContext()));\n                boundsCalculatedCount++;\n                if (startCalled) {\n                    startPostponedEnterTransition();\n                }\n                return true;\n            }\n        };\n        destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);\n    }\n\n    private void getViewBounds(@NonNull final View startView,\n                               @NonNull final View destView,\n                               @NonNull final ViewTreeObserver.OnPreDrawListener preDrawListener) {\n        final ViewBounds viewBounds = new ViewBounds();\n        viewBounds.setDestWidth(destView.getWidth());\n        viewBounds.setDestHeight(destView.getHeight());\n        viewBounds.setDestX(destView.getX());\n        viewBounds.setDestY(destView.getY());\n\n        final Rect destBounds = new Rect();\n        destView.getDrawingRect(destBounds);\n        viewBounds.setDestBounds(destBounds);\n\n        final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();\n\n        final Rect startBounds = new Rect();\n        startView.getDrawingRect(startBounds);\n        viewBounds.setStartBounds(startBounds);\n\n        final int key = destView.hashCode();\n        viewBoundsMap.put(key, viewBounds);\n\n        layoutParams.height = startView.getHeight();\n        layoutParams.width = startView.getWidth();\n\n        destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);\n        destView.requestLayout();\n    }\n\n    // private void animateBounds(@NonNull final View startView,\n    //                            @NonNull final View destView,\n    //                            @NonNull final ViewBounds viewBounds) {\n    //     final ValueAnimator heightAnimator = ObjectAnimator.ofInt(startView.getHeight(), viewBounds.getDestHeight());\n    //     final ValueAnimator widthAnimator = ObjectAnimator.ofInt(startView.getWidth(), viewBounds.getDestWidth());\n    //     heightAnimator.setDuration(DURATION);\n    //     widthAnimator.setDuration(DURATION);\n    //     additionalAnimators.add(heightAnimator);\n    //     additionalAnimators.add(widthAnimator);\n    //     heightAnimator.addUpdateListener(animation -> {\n    //         ViewGroup.LayoutParams params = destView.getLayoutParams();\n    //         params.height = (int) animation.getAnimatedValue();\n    //         destView.requestLayout();\n    //     });\n    //     widthAnimator.addUpdateListener(animation -> {\n    //         ViewGroup.LayoutParams params = destView.getLayoutParams();\n    //         params.width = (int) animation.getAnimatedValue();\n    //         destView.requestLayout();\n    //     });\n    //     onBeforeSharedElementAnimation(startView, destView, viewBounds);\n    //     final float destX = viewBounds.getDestX();\n    //     final float destY = viewBounds.getDestY();\n    //     final AnimatorSet animatorSet = new AnimatorSet();\n    //     animatorSet.addListener(new AnimatorListenerAdapter() {\n    //         @Override\n    //         public void onAnimationEnd(final Animator animation) {\n    //             animationEnded(startView, destView, viewBounds);\n    //         }\n    //     });\n    //\n    //     destView.animate()\n    //             .x(destX)\n    //             .y(destY)\n    //             .setDuration(DURATION)\n    //             .withStartAction(() -> {\n    //                 if (!additionalAnimatorsStarted && additionalAnimators.size() > 0) {\n    //                     additionalAnimatorsStarted = true;\n    //                     animatorSet.playTogether(additionalAnimators);\n    //                     animatorSet.start();\n    //                 }\n    //             })\n    //             .withEndAction(() -> animationEnded(startView, destView, viewBounds))\n    //             .start();\n    // }\n\n    // private int endCount = 0;\n    // private void animationEnded(final View startView, final View destView, final ViewBounds viewBounds) {\n    //     ++endCount;\n    //     if (endCount != startViews.size() + 1) return;\n    //     onEndSharedElementAnimation(startView, destView, viewBounds);\n    // }\n\n    protected void addAnimator(@NonNull final Animator animator) {\n        additionalAnimators.add(animator);\n    }\n\n    protected static class ViewBounds {\n        private float destY;\n        private float destX;\n        private int destHeight;\n        private int destWidth;\n        private Rect startBounds;\n        private Rect destBounds;\n\n        public ViewBounds() {}\n\n        public float getDestY() {\n            return destY;\n        }\n\n        public void setDestY(final float destY) {\n            this.destY = destY;\n        }\n\n        public float getDestX() {\n            return destX;\n        }\n\n        public void setDestX(final float destX) {\n            this.destX = destX;\n        }\n\n        public int getDestHeight() {\n            return destHeight;\n        }\n\n        public void setDestHeight(final int destHeight) {\n            this.destHeight = destHeight;\n        }\n\n        public int getDestWidth() {\n            return destWidth;\n        }\n\n        public void setDestWidth(final int destWidth) {\n            this.destWidth = destWidth;\n        }\n\n        public Rect getStartBounds() {\n            return startBounds;\n        }\n\n        public void setStartBounds(final Rect startBounds) {\n            this.startBounds = startBounds;\n        }\n\n        public Rect getDestBounds() {\n            return destBounds;\n        }\n\n        public void setDestBounds(final Rect destBounds) {\n            this.destBounds = destBounds;\n        }\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        startViews.clear();\n        destViews.clear();\n        viewBoundsMap.clear();\n        additionalAnimators.clear();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/SquareImageView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.appcompat.widget.AppCompatImageView;\n\npublic class SquareImageView extends AppCompatImageView {\n    public SquareImageView(final Context context) {\n        super(context);\n    }\n\n    public SquareImageView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public SquareImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        //noinspection SuspiciousNameCombination\n        super.onMeasure(widthMeasureSpec, widthMeasureSpec);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/TextViewDrawableSize.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.emoji.widget.EmojiAppCompatTextView;\n\nimport awais.instagrabber.R;\n\n/**\n * https://stackoverflow.com/a/31916731\n */\npublic class TextViewDrawableSize extends EmojiAppCompatTextView {\n\n    private int mDrawableWidth;\n    private int mDrawableHeight;\n    private boolean calledFromInit = false;\n\n    public TextViewDrawableSize(final Context context) {\n        this(context, null);\n    }\n\n    public TextViewDrawableSize(final Context context, final AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public TextViewDrawableSize(final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(@NonNull final Context context, final AttributeSet attrs, final int defStyleAttr) {\n        final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextViewDrawableSize, defStyleAttr, 0);\n\n        try {\n            mDrawableWidth = array.getDimensionPixelSize(R.styleable.TextViewDrawableSize_compoundDrawableWidth, -1);\n            mDrawableHeight = array.getDimensionPixelSize(R.styleable.TextViewDrawableSize_compoundDrawableHeight, -1);\n        } finally {\n            array.recycle();\n        }\n\n        if (mDrawableWidth > 0 || mDrawableHeight > 0) {\n            initCompoundDrawableSize();\n        }\n    }\n\n    private void initCompoundDrawableSize() {\n        final Drawable[] drawables = getCompoundDrawablesRelative();\n        for (Drawable drawable : drawables) {\n            if (drawable == null) {\n                continue;\n            }\n\n            final Rect realBounds = drawable.getBounds();\n            float scaleFactor = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth();\n\n            float drawableWidth = drawable.getIntrinsicWidth();\n            float drawableHeight = drawable.getIntrinsicHeight();\n\n            if (mDrawableWidth > 0) {\n                // save scale factor of image\n                if (drawableWidth > mDrawableWidth) {\n                    drawableWidth = mDrawableWidth;\n                    drawableHeight = drawableWidth * scaleFactor;\n                }\n            }\n            if (mDrawableHeight > 0) {\n                // save scale factor of image\n                if (drawableHeight > mDrawableHeight) {\n                    drawableHeight = mDrawableHeight;\n                    drawableWidth = drawableHeight / scaleFactor;\n                }\n            }\n\n            realBounds.right = realBounds.left + Math.round(drawableWidth);\n            realBounds.bottom = realBounds.top + Math.round(drawableHeight);\n\n            drawable.setBounds(realBounds);\n        }\n        setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);\n    }\n\n    public void setCompoundDrawablesRelativeWithSize(@Nullable final Drawable start,\n                                                     @Nullable final Drawable top,\n                                                     @Nullable final Drawable end,\n                                                     @Nullable final Drawable bottom) {\n        setCompoundDrawablesRelative(start, top, end, bottom);\n        initCompoundDrawableSize();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/Tooltip.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.content.Context;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewPropertyAnimator;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.AppCompatTextView;\n\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.utils.ViewUtils;\n\nimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;\n\npublic class Tooltip extends AppCompatTextView {\n\n    private View anchor;\n    private ViewPropertyAnimator animator;\n    private boolean showing;\n\n    private final AppExecutors appExecutors = AppExecutors.INSTANCE;\n    private final Runnable dismissRunnable = () -> {\n        animator = animate().alpha(0).setListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                setVisibility(View.GONE);\n            }\n        }).setDuration(300);\n        animator.start();\n    };\n\n    public Tooltip(@NonNull Context context, @NonNull ViewGroup parentView, int backgroundColor, int textColor) {\n        super(context);\n        setBackgroundDrawable(ViewUtils.createRoundRectDrawable(Utils.convertDpToPx(3), backgroundColor));\n        setTextColor(textColor);\n        setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);\n        setPadding(Utils.convertDpToPx(8), Utils.convertDpToPx(7), Utils.convertDpToPx(8), Utils.convertDpToPx(7));\n        setGravity(Gravity.CENTER_VERTICAL);\n        parentView.addView(this, ViewUtils.createFrame(WRAP_CONTENT, WRAP_CONTENT, Gravity.START | Gravity.TOP, 5, 0, 5, 3));\n        setVisibility(GONE);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        updateTooltipPosition();\n    }\n\n    private void updateTooltipPosition() {\n        if (anchor == null) {\n            return;\n        }\n        int top = 0;\n        int left = 0;\n\n        View containerView = (View) getParent();\n        View view = anchor;\n\n        while (view != containerView) {\n            top += view.getTop();\n            left += view.getLeft();\n            view = (View) view.getParent();\n        }\n        int x = left + anchor.getWidth() / 2 - getMeasuredWidth() / 2;\n        if (x < 0) {\n            x = 0;\n        } else if (x + getMeasuredWidth() > containerView.getMeasuredWidth()) {\n            x = containerView.getMeasuredWidth() - getMeasuredWidth() - Utils.convertDpToPx(16);\n        }\n        setTranslationX(x);\n\n        int y = top - getMeasuredHeight();\n        setTranslationY(y);\n    }\n\n    public void show(View anchor) {\n        if (anchor == null) {\n            return;\n        }\n        this.anchor = anchor;\n        updateTooltipPosition();\n        showing = true;\n\n        appExecutors.getMainThread().cancel(dismissRunnable);\n        appExecutors.getMainThread().execute(dismissRunnable, 2000);\n        if (animator != null) {\n            animator.setListener(null);\n            animator.cancel();\n            animator = null;\n        }\n        if (getVisibility() != VISIBLE) {\n            setAlpha(0f);\n            setVisibility(VISIBLE);\n            animator = animate().setDuration(300).alpha(1f).setListener(null);\n            animator.start();\n        }\n    }\n\n    public void hide() {\n        if (showing) {\n            if (animator != null) {\n                animator.setListener(null);\n                animator.cancel();\n                animator = null;\n            }\n\n            appExecutors.getMainThread().cancel(dismissRunnable);\n            dismissRunnable.run();\n        }\n        showing = false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/UsernameTextView.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.util.AttributeSet;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.appcompat.widget.AppCompatTextView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.Utils;\n\npublic class UsernameTextView extends AppCompatTextView {\n    private static final String TAG = UsernameTextView.class.getSimpleName();\n\n    private final int drawableSize = Utils.convertDpToPx(24);\n\n    private boolean verified;\n    private VerticalImageSpan verifiedSpan;\n\n    public UsernameTextView(@NonNull final Context context) {\n        this(context, null);\n    }\n\n    public UsernameTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public UsernameTextView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        try {\n            final Drawable verifiedDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.verified);\n            final Drawable drawable = verifiedDrawable.mutate();\n            drawable.setBounds(0, 0, drawableSize, drawableSize);\n            verifiedSpan = new VerticalImageSpan(drawable);\n        } catch (Exception e) {\n            Log.e(TAG, \"init: \", e);\n        }\n    }\n\n    public void setUsername(final CharSequence username) {\n        setUsername(username, false);\n    }\n\n    public void setUsername(final CharSequence username, final boolean verified) {\n        this.verified = verified;\n        final SpannableStringBuilder sb = new SpannableStringBuilder(username);\n        if (verified) {\n            try {\n                if (verifiedSpan != null) {\n                    sb.append(\"  \");\n                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"bind: \", e);\n            }\n        }\n        super.setText(sb);\n    }\n\n    public boolean isVerified() {\n        return verified;\n    }\n\n    public void setVerified(final boolean verified) {\n        setUsername(getText(), verified);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewParent;\n\nimport androidx.annotation.NonNull;\n\npublic class VerticalDragHelper {\n    // private static final String TAG = \"VerticalDragHelper\";\n    private static final double SWIPE_THRESHOLD_VELOCITY = 80;\n\n    private final View view;\n\n    private GestureDetector gestureDetector;\n    private Context context;\n    private double flingVelocity;\n    private OnVerticalDragListener onVerticalDragListener;\n\n    private final GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {\n\n        @Override\n        public boolean onSingleTapConfirmed(final MotionEvent e) {\n            view.performClick();\n            return true;\n        }\n\n        @Override\n        public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {\n            double yDir = e1.getY() - e2.getY();\n            // Log.d(TAG, \"onFling: yDir: \" + yDir);\n            if (yDir < -SWIPE_THRESHOLD_VELOCITY || yDir > SWIPE_THRESHOLD_VELOCITY) {\n                flingVelocity = yDir;\n            }\n            return super.onFling(e1, e2, velocityX, velocityY);\n        }\n    };\n\n    private float prevRawY;\n    private boolean isDragging;\n    private float prevRawX;\n    private float dX;\n    private float prevDY;\n\n    public VerticalDragHelper(@NonNull final View view) {\n        this.view = view;\n        final Context context = view.getContext();\n        if (context == null) return;\n        this.context = context;\n        init();\n    }\n\n    public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {\n        this.onVerticalDragListener = onVerticalDragListener;\n    }\n\n    protected void init() {\n        gestureDetector = new GestureDetector(context, gestureListener);\n    }\n\n    public boolean onDragTouch(final MotionEvent event) {\n        if (onVerticalDragListener == null) {\n            return false;\n        }\n        if (gestureDetector.onTouchEvent(event)) {\n            return true;\n        }\n        switch (event.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                return true;\n            case MotionEvent.ACTION_MOVE:\n                boolean handled = false;\n                final float rawY = event.getRawY();\n                final float dY = rawY - prevRawY;\n                if (!isDragging) {\n                    final float rawX = event.getRawX();\n                    if (prevRawX != 0) {\n                        dX = rawX - prevRawX;\n                    }\n                    prevRawX = rawX;\n                    if (prevRawY != 0) {\n                        final float dYAbs = Math.abs(dY - prevDY);\n                        if (!isDragging && dYAbs < 50) {\n                            final float abs = Math.abs(dY) - Math.abs(dX);\n                            if (abs > 0) {\n                                isDragging = true;\n                            }\n                        }\n                    }\n                }\n                if (isDragging) {\n                    final ViewParent parent = view.getParent();\n                    parent.requestDisallowInterceptTouchEvent(true);\n                    onVerticalDragListener.onDrag(dY);\n                    handled = true;\n                }\n                prevDY = dY;\n                prevRawY = rawY;\n                return handled;\n            case MotionEvent.ACTION_UP:\n                // Log.d(TAG, \"onDragTouch: reset prevRawY\");\n                prevRawY = 0;\n                if (flingVelocity != 0) {\n                    onVerticalDragListener.onFling(flingVelocity);\n                    flingVelocity = 0;\n                    isDragging = false;\n                    return true;\n                }\n                if (isDragging) {\n                    onVerticalDragListener.onDragEnd();\n                    isDragging = false;\n                    return true;\n                }\n                return false;\n            default:\n                return false;\n        }\n    }\n\n    public boolean isDragging() {\n        return isDragging;\n    }\n\n    public boolean onGestureTouchEvent(final MotionEvent event) {\n        return gestureDetector.onTouchEvent(event);\n    }\n\n    public interface OnVerticalDragListener {\n        void onDrag(final float dY);\n\n        void onDragEnd();\n\n        void onFling(final double flingVelocity);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/VerticalImageSpan.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.text.style.ImageSpan;\n\nimport androidx.annotation.NonNull;\n\npublic class VerticalImageSpan extends ImageSpan {\n\n    public VerticalImageSpan(final Drawable drawable) {\n        super(drawable);\n    }\n\n    /**\n     * update the text line height\n     */\n    @Override\n    public int getSize(@NonNull Paint paint,\n                       CharSequence text,\n                       int start,\n                       int end,\n                       Paint.FontMetricsInt fontMetricsInt) {\n        Drawable drawable = getDrawable();\n        Rect rect = drawable.getBounds();\n        if (fontMetricsInt != null) {\n            Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();\n            int fontHeight = fmPaint.descent - fmPaint.ascent;\n            int drHeight = rect.bottom - rect.top;\n            int centerY = fmPaint.ascent + fontHeight / 2;\n\n            fontMetricsInt.ascent = centerY - drHeight / 2;\n            fontMetricsInt.top = fontMetricsInt.ascent;\n            fontMetricsInt.bottom = centerY + drHeight / 2;\n            fontMetricsInt.descent = fontMetricsInt.bottom;\n        }\n        return rect.right;\n    }\n\n    /**\n     * see detail message in android.text.TextLine\n     *\n     * @param canvas the canvas, can be null if not rendering\n     * @param text   the text to be draw\n     * @param start  the text start position\n     * @param end    the text end position\n     * @param x      the edge of the replacement closest to the leading margin\n     * @param top    the top of the line\n     * @param y      the baseline\n     * @param bottom the bottom of the line\n     * @param paint  the work paint\n     */\n    @Override\n    public void draw(Canvas canvas,\n                     CharSequence text,\n                     int start,\n                     int end,\n                     float x,\n                     int top,\n                     int y,\n                     int bottom,\n                     Paint paint) {\n        Drawable drawable = getDrawable();\n        canvas.save();\n        Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();\n        int fontHeight = fmPaint.descent - fmPaint.ascent;\n        int centerY = y + fmPaint.descent - fontHeight / 2;\n        int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;\n        canvas.translate(x, transY);\n        drawable.draw(canvas);\n        canvas.restore();\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java",
    "content": "package awais.instagrabber.customviews;\n\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\n\npublic class VideoPlayerCallbackAdapter implements VideoPlayerViewHelper.VideoPlayerCallback {\n    @Override\n    public void onThumbnailLoaded() {}\n\n    @Override\n    public void onThumbnailClick() {}\n\n    @Override\n    public void onPlayerViewLoaded() {}\n\n    @Override\n    public void onPlay() {}\n\n    @Override\n    public void onPause() {}\n\n    @Override\n    public void onRelease() {}\n\n    @Override\n    public void onFullScreenModeChanged(final boolean isFullScreen, final StyledPlayerView playerView) {}\n\n    @Override\n    public boolean isInFullScreen() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java",
    "content": "package awais.instagrabber.customviews;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.graphics.drawable.Animatable;\nimport android.net.Uri;\nimport android.os.Looper;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageButton;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.imagepipeline.image.ImageInfo;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\nimport com.google.android.exoplayer2.ExoPlaybackException;\nimport com.google.android.exoplayer2.MediaItem;\nimport com.google.android.exoplayer2.Player;\nimport com.google.android.exoplayer2.SimpleExoPlayer;\nimport com.google.android.exoplayer2.audio.AudioListener;\nimport com.google.android.exoplayer2.source.ProgressiveMediaSource;\nimport com.google.android.exoplayer2.source.TrackGroupArray;\nimport com.google.android.exoplayer2.trackselection.TrackSelectionArray;\nimport com.google.android.exoplayer2.ui.AspectRatioFrameLayout;\nimport com.google.android.exoplayer2.ui.StyledPlayerControlView;\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\nimport com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;\nimport awais.instagrabber.utils.Utils;\n\npublic class VideoPlayerViewHelper implements Player.EventListener {\n    private static final String TAG = VideoPlayerViewHelper.class.getSimpleName();\n\n    private final Context context;\n    private final LayoutVideoPlayerWithThumbnailBinding binding;\n    private final float initialVolume;\n    private final float thumbnailAspectRatio;\n    private final String thumbnailUrl;\n    private final boolean loadPlayerOnClick;\n    private final VideoPlayerCallback videoPlayerCallback;\n    private final String videoUrl;\n    private final DefaultDataSourceFactory dataSourceFactory;\n    private SimpleExoPlayer player;\n    private AppCompatImageButton mute;\n\n    private final AudioListener audioListener = new AudioListener() {\n        @Override\n        public void onVolumeChanged(final float volume) {\n            updateMuteIcon(volume);\n        }\n    };\n    private final View.OnClickListener muteOnClickListener = v -> toggleMute();\n    private Object layoutManager;\n\n    public VideoPlayerViewHelper(@NonNull final Context context,\n                                 @NonNull final LayoutVideoPlayerWithThumbnailBinding binding,\n                                 @NonNull final String videoUrl,\n                                 final float initialVolume,\n                                 final float thumbnailAspectRatio,\n                                 final String thumbnailUrl,\n                                 final boolean loadPlayerOnClick,\n                                 final VideoPlayerCallback videoPlayerCallback) {\n        this.context = context;\n        this.binding = binding;\n        this.initialVolume = initialVolume;\n        this.thumbnailAspectRatio = thumbnailAspectRatio;\n        this.thumbnailUrl = thumbnailUrl;\n        this.loadPlayerOnClick = loadPlayerOnClick;\n        this.videoPlayerCallback = videoPlayerCallback;\n        this.videoUrl = videoUrl;\n        this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), \"instagram\");\n        bind();\n    }\n\n    private void bind() {\n        binding.thumbnailParent.setOnClickListener(v -> {\n            if (videoPlayerCallback != null) {\n                videoPlayerCallback.onThumbnailClick();\n            }\n            if (loadPlayerOnClick) {\n                loadPlayer();\n            }\n        });\n        setThumbnail();\n    }\n\n    private void setThumbnail() {\n        binding.thumbnail.setAspectRatio(thumbnailAspectRatio);\n        ImageRequest thumbnailRequest = null;\n        if (thumbnailUrl != null) {\n            thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl)).build();\n        }\n        final PipelineDraweeControllerBuilder builder = Fresco\n                .newDraweeControllerBuilder()\n                .setControllerListener(new BaseControllerListener<ImageInfo>() {\n                    @Override\n                    public void onFailure(final String id, final Throwable throwable) {\n                        if (videoPlayerCallback != null) {\n                            videoPlayerCallback.onThumbnailLoaded();\n                        }\n                    }\n\n                    @Override\n                    public void onFinalImageSet(final String id,\n                                                final ImageInfo imageInfo,\n                                                final Animatable animatable) {\n                        if (videoPlayerCallback != null) {\n                            videoPlayerCallback.onThumbnailLoaded();\n                        }\n                    }\n                });\n        if (thumbnailRequest != null) {\n            builder.setImageRequest(thumbnailRequest);\n        }\n        binding.thumbnail.setController(builder.build());\n    }\n\n    private void loadPlayer() {\n        if (videoUrl == null) return;\n        if (binding.getRoot().getDisplayedChild() == 0) {\n            binding.getRoot().showNext();\n        }\n        if (videoPlayerCallback != null) {\n            videoPlayerCallback.onPlayerViewLoaded();\n        }\n        player = (SimpleExoPlayer) binding.playerView.getPlayer();\n        if (player != null) {\n            player.release();\n        }\n        final ViewGroup.LayoutParams playerViewLayoutParams = binding.playerView.getLayoutParams();\n        if (playerViewLayoutParams.height > Utils.displayMetrics.heightPixels * 0.8) {\n            playerViewLayoutParams.height = (int) (Utils.displayMetrics.heightPixels * 0.8);\n        }\n        player = new SimpleExoPlayer.Builder(context)\n                .setLooper(Looper.getMainLooper())\n                .build();\n        player.addListener(this);\n        player.addAudioListener(audioListener);\n        player.setVolume(initialVolume);\n        player.setPlayWhenReady(true);\n        player.setRepeatMode(Player.REPEAT_MODE_ALL);\n        final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);\n        final MediaItem mediaItem = MediaItem.fromUri(videoUrl);\n        final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);\n        player.setMediaSource(mediaSource);\n        player.prepare();\n        binding.playerView.setPlayer(player);\n        binding.playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);\n        binding.playerView.setShowNextButton(false);\n        binding.playerView.setShowPreviousButton(false);\n        binding.playerView.setControllerOnFullScreenModeChangedListener(isFullScreen -> {\n            if (videoPlayerCallback == null) return;\n            videoPlayerCallback.onFullScreenModeChanged(isFullScreen, binding.playerView);\n        });\n        setupControllerView();\n    }\n\n    private void setupControllerView() {\n        try {\n            final StyledPlayerControlView controllerView = getStyledPlayerControlView();\n            if (controllerView == null) return;\n            layoutManager = setControlViewLayoutManager(controllerView);\n            if (videoPlayerCallback != null && videoPlayerCallback.isInFullScreen()) {\n                setControllerViewToFullScreenMode(controllerView);\n            }\n            final ViewGroup exoBasicControls = controllerView.findViewById(R.id.exo_basic_controls);\n            if (exoBasicControls == null) return;\n            mute = new AppCompatImageButton(context);\n            final Resources resources = context.getResources();\n            if (resources == null) return;\n            final int width = resources.getDimensionPixelSize(R.dimen.exo_small_icon_width);\n            final int height = resources.getDimensionPixelSize(R.dimen.exo_small_icon_height);\n            final int margin = resources.getDimensionPixelSize(R.dimen.exo_small_icon_horizontal_margin);\n            final int paddingHorizontal = resources.getDimensionPixelSize(R.dimen.exo_small_icon_padding_horizontal);\n            final int paddingVertical = resources.getDimensionPixelSize(R.dimen.exo_small_icon_padding_vertical);\n            final ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams(width, height);\n            layoutParams.setMargins(margin, 0, margin, 0);\n            mute.setLayoutParams(layoutParams);\n            mute.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical);\n            mute.setScaleType(ImageView.ScaleType.FIT_XY);\n            mute.setBackgroundResource(Utils.getAttrResId(context, android.R.attr.selectableItemBackground));\n            mute.setImageTintList(ColorStateList.valueOf(resources.getColor(R.color.white)));\n            updateMuteIcon(player.getVolume());\n            exoBasicControls.addView(mute, 0);\n            mute.setOnClickListener(muteOnClickListener);\n        } catch (Exception e) {\n            Log.e(TAG, \"loadPlayer: \", e);\n        }\n    }\n\n    @Nullable\n    private Object setControlViewLayoutManager(@NonNull final StyledPlayerControlView controllerView)\n            throws NoSuchFieldException, IllegalAccessException {\n        final Field controlViewLayoutManagerField = controllerView.getClass().getDeclaredField(\"controlViewLayoutManager\");\n        controlViewLayoutManagerField.setAccessible(true);\n        return controlViewLayoutManagerField.get(controllerView);\n    }\n\n    private void setControllerViewToFullScreenMode(@NonNull final StyledPlayerControlView controllerView)\n            throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {\n        // Exoplayer doesn't expose the fullscreen state, so using reflection\n        final Field fullScreenButtonField = controllerView.getClass().getDeclaredField(\"fullScreenButton\");\n        fullScreenButtonField.setAccessible(true);\n        final ImageView fullScreenButton = (ImageView) fullScreenButtonField.get(controllerView);\n        final Field isFullScreen = controllerView.getClass().getDeclaredField(\"isFullScreen\");\n        isFullScreen.setAccessible(true);\n        isFullScreen.set(controllerView, true);\n        final Method updateFullScreenButtonForState = controllerView\n                .getClass()\n                .getDeclaredMethod(\"updateFullScreenButtonForState\", ImageView.class, boolean.class);\n        updateFullScreenButtonForState.setAccessible(true);\n        updateFullScreenButtonForState.invoke(controllerView, fullScreenButton, true);\n\n    }\n\n    @Nullable\n    private StyledPlayerControlView getStyledPlayerControlView() throws NoSuchFieldException, IllegalAccessException {\n        final Field controller = binding.playerView.getClass().getDeclaredField(\"controller\");\n        controller.setAccessible(true);\n        return (StyledPlayerControlView) controller.get(binding.playerView);\n    }\n\n    @Override\n    public void onTracksChanged(@NonNull TrackGroupArray trackGroups, @NonNull TrackSelectionArray trackSelections) {\n        if (trackGroups.isEmpty()) {\n            setHasAudio(false);\n            return;\n        }\n        boolean hasAudio = false;\n        for (int i = 0; i < trackGroups.length; i++) {\n            for (int g = 0; g < trackGroups.get(i).length; g++) {\n                final String sampleMimeType = trackGroups.get(i).getFormat(g).sampleMimeType;\n                if (sampleMimeType != null && sampleMimeType.contains(\"audio\")) {\n                    hasAudio = true;\n                    break;\n                }\n            }\n        }\n        setHasAudio(hasAudio);\n    }\n\n    private void setHasAudio(final boolean hasAudio) {\n        if (mute == null) return;\n        mute.setEnabled(hasAudio);\n        mute.setAlpha(hasAudio ? 1f : 0.5f);\n        updateMuteIcon(hasAudio ? 1f : 0f);\n    }\n\n    private void updateMuteIcon(final float volume) {\n        if (mute == null) return;\n        if (volume == 0) {\n            mute.setImageResource(R.drawable.ic_volume_off_24);\n            return;\n        }\n        mute.setImageResource(R.drawable.ic_volume_up_24);\n    }\n\n    @Override\n    public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) {\n        if (videoPlayerCallback == null) return;\n        if (playWhenReady) {\n            videoPlayerCallback.onPlay();\n            return;\n        }\n        videoPlayerCallback.onPause();\n    }\n\n    @Override\n    public void onPlayerError(@NonNull final ExoPlaybackException error) {\n        Log.e(TAG, \"onPlayerError\", error);\n    }\n\n    private void toggleMute() {\n        if (player == null) return;\n        if (layoutManager != null) {\n            try {\n                final Method resetHideCallbacks = layoutManager.getClass().getDeclaredMethod(\"resetHideCallbacks\");\n                resetHideCallbacks.invoke(layoutManager);\n            } catch (Exception e) {\n                Log.e(TAG, \"toggleMute: \", e);\n            }\n        }\n        final float vol = player.getVolume() == 0f ? 1f : 0f;\n        player.setVolume(vol);\n    }\n\n    public void releasePlayer() {\n        if (videoPlayerCallback != null) {\n            videoPlayerCallback.onRelease();\n        }\n        if (player != null) {\n            player.release();\n            player = null;\n        }\n    }\n\n    public void pause() {\n        if (player != null) {\n            player.pause();\n        }\n    }\n\n    public interface VideoPlayerCallback {\n        void onThumbnailLoaded();\n\n        void onThumbnailClick();\n\n        void onPlayerViewLoaded();\n\n        void onPlay();\n\n        void onPause();\n\n        void onRelease();\n\n        void onFullScreenModeChanged(boolean isFullScreen, final StyledPlayerView playerView);\n\n        boolean isInFullScreen();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/AbstractAnimatedZoomableController.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.graphics.Matrix;\nimport android.graphics.PointF;\n\nimport androidx.annotation.Nullable;\n\nimport com.facebook.common.logging.FLog;\n\n/**\n * Abstract class for ZoomableController that adds animation capabilities to\n * DefaultZoomableController.\n */\npublic abstract class AbstractAnimatedZoomableController extends DefaultZoomableController {\n\n    private boolean mIsAnimating;\n    private final float[] mStartValues = new float[9];\n    private final float[] mStopValues = new float[9];\n    private final float[] mCurrentValues = new float[9];\n    private final Matrix mNewTransform = new Matrix();\n    private final Matrix mWorkingTransform = new Matrix();\n\n    public AbstractAnimatedZoomableController(TransformGestureDetector transformGestureDetector) {\n        super(transformGestureDetector);\n    }\n\n    @Override\n    public void reset() {\n        FLog.v(getLogTag(), \"reset\");\n        stopAnimation();\n        mWorkingTransform.reset();\n        mNewTransform.reset();\n        super.reset();\n    }\n\n    /**\n     * Returns true if the zoomable transform is identity matrix, and the controller is idle.\n     */\n    @Override\n    public boolean isIdentity() {\n        return !isAnimating() && super.isIdentity();\n    }\n\n    /**\n     * Zooms to the desired scale and positions the image so that the given image point corresponds to\n     * the given view point.\n     *\n     * <p>If this method is called while an animation or gesture is already in progress, the current\n     * animation or gesture will be stopped first.\n     *\n     * @param scale      desired scale, will be limited to {min, max} scale factor\n     * @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)\n     * @param viewPoint  2D point in view's absolute coordinate system\n     */\n    @Override\n    public void zoomToPoint(float scale, PointF imagePoint, PointF viewPoint) {\n        zoomToPoint(scale, imagePoint, viewPoint, LIMIT_ALL, 0, null);\n    }\n\n    /**\n     * Zooms to the desired scale and positions the image so that the given image point corresponds to\n     * the given view point.\n     *\n     * <p>If this method is called while an animation or gesture is already in progress, the current\n     * animation or gesture will be stopped first.\n     *\n     * @param scale               desired scale, will be limited to {min, max} scale factor\n     * @param imagePoint          2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)\n     * @param viewPoint           2D point in view's absolute coordinate system\n     * @param limitFlags          whether to limit translation and/or scale.\n     * @param durationMs          length of animation of the zoom, or 0 if no animation desired\n     * @param onAnimationComplete code to run when the animation completes. Ignored if durationMs=0\n     */\n    public void zoomToPoint(\n            float scale,\n            PointF imagePoint,\n            PointF viewPoint,\n            @LimitFlag int limitFlags,\n            long durationMs,\n            @Nullable Runnable onAnimationComplete) {\n        FLog.v(getLogTag(), \"zoomToPoint: duration %d ms\", durationMs);\n        calculateZoomToPointTransform(mNewTransform, scale, imagePoint, viewPoint, limitFlags);\n        setTransform(mNewTransform, durationMs, onAnimationComplete);\n    }\n\n    /**\n     * Sets a new zoomable transformation and animates to it if desired.\n     *\n     * <p>If this method is called while an animation or gesture is already in progress, the current\n     * animation or gesture will be stopped first.\n     *\n     * @param newTransform        new transform to make active\n     * @param durationMs          duration of the animation, or 0 to not animate\n     * @param onAnimationComplete code to run when the animation completes. Ignored if durationMs=0\n     */\n    public void setTransform(\n            Matrix newTransform, long durationMs, @Nullable Runnable onAnimationComplete) {\n        FLog.v(getLogTag(), \"setTransform: duration %d ms\", durationMs);\n        if (durationMs <= 0) {\n            setTransformImmediate(newTransform);\n        } else {\n            setTransformAnimated(newTransform, durationMs, onAnimationComplete);\n        }\n    }\n\n    private void setTransformImmediate(final Matrix newTransform) {\n        FLog.v(getLogTag(), \"setTransformImmediate\");\n        stopAnimation();\n        mWorkingTransform.set(newTransform);\n        super.setTransform(newTransform);\n        getDetector().restartGesture();\n    }\n\n    protected boolean isAnimating() {\n        return mIsAnimating;\n    }\n\n    protected void setAnimating(boolean isAnimating) {\n        mIsAnimating = isAnimating;\n    }\n\n    protected float[] getStartValues() {\n        return mStartValues;\n    }\n\n    protected float[] getStopValues() {\n        return mStopValues;\n    }\n\n    protected Matrix getWorkingTransform() {\n        return mWorkingTransform;\n    }\n\n    @Override\n    public void onGestureBegin(TransformGestureDetector detector) {\n        FLog.v(getLogTag(), \"onGestureBegin\");\n        stopAnimation();\n        super.onGestureBegin(detector);\n    }\n\n    @Override\n    public void onGestureUpdate(TransformGestureDetector detector) {\n        FLog.v(getLogTag(), \"onGestureUpdate %s\", isAnimating() ? \"(ignored)\" : \"\");\n        if (isAnimating()) {\n            return;\n        }\n        super.onGestureUpdate(detector);\n    }\n\n    protected void calculateInterpolation(Matrix outMatrix, float fraction) {\n        for (int i = 0; i < 9; i++) {\n            mCurrentValues[i] = (1 - fraction) * mStartValues[i] + fraction * mStopValues[i];\n        }\n        outMatrix.setValues(mCurrentValues);\n    }\n\n    public abstract void setTransformAnimated(\n            final Matrix newTransform, long durationMs, @Nullable final Runnable onAnimationComplete);\n\n    protected abstract void stopAnimation();\n\n    protected abstract Class<?> getLogTag();\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/AnimatedZoomableController.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.annotation.SuppressLint;\nimport android.graphics.Matrix;\nimport android.view.animation.DecelerateInterpolator;\n\nimport androidx.annotation.Nullable;\n\nimport com.facebook.common.internal.Preconditions;\nimport com.facebook.common.logging.FLog;\n\n\n/**\n * ZoomableController that adds animation capabilities to DefaultZoomableController using standard\n * Android animation classes\n */\npublic class AnimatedZoomableController extends AbstractAnimatedZoomableController {\n\n    private static final Class<?> TAG = AnimatedZoomableController.class;\n\n    private final ValueAnimator mValueAnimator;\n\n    public static AnimatedZoomableController newInstance() {\n        return new AnimatedZoomableController(TransformGestureDetector.newInstance());\n    }\n\n    @SuppressLint(\"NewApi\")\n    public AnimatedZoomableController(TransformGestureDetector transformGestureDetector) {\n        super(transformGestureDetector);\n        mValueAnimator = ValueAnimator.ofFloat(0, 1);\n        mValueAnimator.setInterpolator(new DecelerateInterpolator());\n    }\n\n    @SuppressLint(\"NewApi\")\n    @Override\n    public void setTransformAnimated(\n            final Matrix newTransform, long durationMs, @Nullable final Runnable onAnimationComplete) {\n        FLog.v(getLogTag(), \"setTransformAnimated: duration %d ms\", durationMs);\n        stopAnimation();\n        Preconditions.checkArgument(durationMs > 0);\n        Preconditions.checkState(!isAnimating());\n        setAnimating(true);\n        mValueAnimator.setDuration(durationMs);\n        getTransform().getValues(getStartValues());\n        newTransform.getValues(getStopValues());\n        mValueAnimator.addUpdateListener(\n                new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator valueAnimator) {\n                        calculateInterpolation(getWorkingTransform(), (float) valueAnimator.getAnimatedValue());\n                        AnimatedZoomableController.super.setTransform(getWorkingTransform());\n                    }\n                });\n        mValueAnimator.addListener(\n                new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationCancel(Animator animation) {\n                        FLog.v(getLogTag(), \"setTransformAnimated: animation cancelled\");\n                        onAnimationStopped();\n                    }\n\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        FLog.v(getLogTag(), \"setTransformAnimated: animation finished\");\n                        onAnimationStopped();\n                    }\n\n                    private void onAnimationStopped() {\n                        if (onAnimationComplete != null) {\n                            onAnimationComplete.run();\n                        }\n                        setAnimating(false);\n                        getDetector().restartGesture();\n                    }\n                });\n        mValueAnimator.start();\n    }\n\n    @SuppressLint(\"NewApi\")\n    @Override\n    public void stopAnimation() {\n        if (!isAnimating()) {\n            return;\n        }\n        FLog.v(getLogTag(), \"stopAnimation\");\n        mValueAnimator.cancel();\n        mValueAnimator.removeAllUpdateListeners();\n        mValueAnimator.removeAllListeners();\n    }\n\n    @Override\n    protected Class<?> getLogTag() {\n        return TAG;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/DefaultZoomableController.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.graphics.Matrix;\nimport android.graphics.PointF;\nimport android.graphics.RectF;\nimport android.view.MotionEvent;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.Nullable;\n\nimport com.facebook.common.logging.FLog;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Zoomable controller that calculates transformation based on touch events.\n */\npublic class DefaultZoomableController\n        implements ZoomableController, TransformGestureDetector.Listener {\n\n    /**\n     * Interface for handling call backs when the image bounds are set.\n     */\n    public interface ImageBoundsListener {\n        void onImageBoundsSet(RectF imageBounds);\n    }\n\n    @IntDef(\n            flag = true,\n            value = {LIMIT_NONE, LIMIT_TRANSLATION_X, LIMIT_TRANSLATION_Y, LIMIT_SCALE, LIMIT_ALL})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface LimitFlag {}\n\n    public static final int LIMIT_NONE = 0;\n    public static final int LIMIT_TRANSLATION_X = 1;\n    public static final int LIMIT_TRANSLATION_Y = 2;\n    public static final int LIMIT_SCALE = 4;\n    public static final int LIMIT_ALL = LIMIT_TRANSLATION_X | LIMIT_TRANSLATION_Y | LIMIT_SCALE;\n\n    private static final float EPS = 1e-3f;\n\n    private static final Class<?> TAG = DefaultZoomableController.class;\n\n    private static final RectF IDENTITY_RECT = new RectF(0, 0, 1, 1);\n\n    private TransformGestureDetector mGestureDetector;\n\n    private @Nullable\n    ImageBoundsListener mImageBoundsListener;\n\n    private @Nullable\n    Listener mListener = null;\n\n    private boolean mIsEnabled = false;\n    private boolean mIsRotationEnabled = false;\n    private boolean mIsScaleEnabled = true;\n    private boolean mIsTranslationEnabled = true;\n    private boolean mIsGestureZoomEnabled = true;\n\n    private float mMinScaleFactor = 1.0f;\n    private float mMaxScaleFactor = 2.0f;\n\n    // View bounds, in view-absolute coordinates\n    private final RectF mViewBounds = new RectF();\n    // Non-transformed image bounds, in view-absolute coordinates\n    private final RectF mImageBounds = new RectF();\n    // Transformed image bounds, in view-absolute coordinates\n    private final RectF mTransformedImageBounds = new RectF();\n\n    private final Matrix mPreviousTransform = new Matrix();\n    private final Matrix mActiveTransform = new Matrix();\n    private final Matrix mActiveTransformInverse = new Matrix();\n    private final float[] mTempValues = new float[9];\n    private final RectF mTempRect = new RectF();\n    private boolean mWasTransformCorrected;\n\n    public static DefaultZoomableController newInstance() {\n        return new DefaultZoomableController(TransformGestureDetector.newInstance());\n    }\n\n    public DefaultZoomableController(TransformGestureDetector gestureDetector) {\n        mGestureDetector = gestureDetector;\n        mGestureDetector.setListener(this);\n    }\n\n    /**\n     * Rests the controller.\n     */\n    public void reset() {\n        FLog.v(TAG, \"reset\");\n        mGestureDetector.reset();\n        mPreviousTransform.reset();\n        mActiveTransform.reset();\n        onTransformChanged();\n    }\n\n    /**\n     * Sets the zoomable listener.\n     */\n    @Override\n    public void setListener(Listener listener) {\n        mListener = listener;\n    }\n\n    /**\n     * Sets whether the controller is enabled or not.\n     */\n    @Override\n    public void setEnabled(boolean enabled) {\n        mIsEnabled = enabled;\n        if (!enabled) {\n            reset();\n        }\n    }\n\n    /**\n     * Gets whether the controller is enabled or not.\n     */\n    @Override\n    public boolean isEnabled() {\n        return mIsEnabled;\n    }\n\n    /**\n     * Sets whether the rotation gesture is enabled or not.\n     */\n    public void setRotationEnabled(boolean enabled) {\n        mIsRotationEnabled = enabled;\n    }\n\n    /**\n     * Gets whether the rotation gesture is enabled or not.\n     */\n    public boolean isRotationEnabled() {\n        return mIsRotationEnabled;\n    }\n\n    /**\n     * Sets whether the scale gesture is enabled or not.\n     */\n    public void setScaleEnabled(boolean enabled) {\n        mIsScaleEnabled = enabled;\n    }\n\n    /**\n     * Gets whether the scale gesture is enabled or not.\n     */\n    public boolean isScaleEnabled() {\n        return mIsScaleEnabled;\n    }\n\n    /**\n     * Sets whether the translation gesture is enabled or not.\n     */\n    public void setTranslationEnabled(boolean enabled) {\n        mIsTranslationEnabled = enabled;\n    }\n\n    /**\n     * Gets whether the translations gesture is enabled or not.\n     */\n    public boolean isTranslationEnabled() {\n        return mIsTranslationEnabled;\n    }\n\n    /**\n     * Sets the minimum scale factor allowed.\n     *\n     * <p>Hierarchy's scaling (if any) is not taken into account.\n     */\n    public void setMinScaleFactor(float minScaleFactor) {\n        mMinScaleFactor = minScaleFactor;\n    }\n\n    /**\n     * Gets the minimum scale factor allowed.\n     */\n    public float getMinScaleFactor() {\n        return mMinScaleFactor;\n    }\n\n    /**\n     * Sets the maximum scale factor allowed.\n     *\n     * <p>Hierarchy's scaling (if any) is not taken into account.\n     */\n    public void setMaxScaleFactor(float maxScaleFactor) {\n        mMaxScaleFactor = maxScaleFactor;\n    }\n\n    /**\n     * Gets the maximum scale factor allowed.\n     */\n    public float getMaxScaleFactor() {\n        return mMaxScaleFactor;\n    }\n\n    /**\n     * Sets whether gesture zooms are enabled or not.\n     */\n    public void setGestureZoomEnabled(boolean isGestureZoomEnabled) {\n        mIsGestureZoomEnabled = isGestureZoomEnabled;\n    }\n\n    /**\n     * Gets whether gesture zooms are enabled or not.\n     */\n    public boolean isGestureZoomEnabled() {\n        return mIsGestureZoomEnabled;\n    }\n\n    /**\n     * Gets the current scale factor.\n     */\n    @Override\n    public float getScaleFactor() {\n        return getMatrixScaleFactor(mActiveTransform);\n    }\n\n    /**\n     * Sets the image bounds, in view-absolute coordinates.\n     */\n    @Override\n    public void setImageBounds(RectF imageBounds) {\n        if (!imageBounds.equals(mImageBounds)) {\n            mImageBounds.set(imageBounds);\n            onTransformChanged();\n            if (mImageBoundsListener != null) {\n                mImageBoundsListener.onImageBoundsSet(mImageBounds);\n            }\n        }\n    }\n\n    /**\n     * Gets the non-transformed image bounds, in view-absolute coordinates.\n     */\n    public RectF getImageBounds() {\n        return mImageBounds;\n    }\n\n    /**\n     * Gets the transformed image bounds, in view-absolute coordinates\n     */\n    private RectF getTransformedImageBounds() {\n        return mTransformedImageBounds;\n    }\n\n    /**\n     * Sets the view bounds.\n     */\n    @Override\n    public void setViewBounds(RectF viewBounds) {\n        mViewBounds.set(viewBounds);\n    }\n\n    /**\n     * Gets the view bounds.\n     */\n    public RectF getViewBounds() {\n        return mViewBounds;\n    }\n\n    /**\n     * Sets the image bounds listener.\n     */\n    public void setImageBoundsListener(@Nullable ImageBoundsListener imageBoundsListener) {\n        mImageBoundsListener = imageBoundsListener;\n    }\n\n    /**\n     * Gets the image bounds listener.\n     */\n    public @Nullable\n    ImageBoundsListener getImageBoundsListener() {\n        return mImageBoundsListener;\n    }\n\n    /**\n     * Returns true if the zoomable transform is identity matrix.\n     */\n    @Override\n    public boolean isIdentity() {\n        return isMatrixIdentity(mActiveTransform, 1e-3f);\n    }\n\n    /**\n     * Returns true if the transform was corrected during the last update.\n     *\n     * <p>We should rename this method to `wasTransformedWithoutCorrection` and just return the\n     * internal flag directly. However, this requires interface change and negation of meaning.\n     */\n    @Override\n    public boolean wasTransformCorrected() {\n        return mWasTransformCorrected;\n    }\n\n    /**\n     * Gets the matrix that transforms image-absolute coordinates to view-absolute coordinates. The\n     * zoomable transformation is taken into account.\n     *\n     * <p>Internal matrix is exposed for performance reasons and is not to be modified by the callers.\n     */\n    @Override\n    public Matrix getTransform() {\n        return mActiveTransform;\n    }\n\n    /**\n     * Gets the matrix that transforms image-relative coordinates to view-absolute coordinates. The\n     * zoomable transformation is taken into account.\n     */\n    public void getImageRelativeToViewAbsoluteTransform(Matrix outMatrix) {\n        outMatrix.setRectToRect(IDENTITY_RECT, mTransformedImageBounds, Matrix.ScaleToFit.FILL);\n    }\n\n    /**\n     * Maps point from view-absolute to image-relative coordinates. This takes into account the\n     * zoomable transformation.\n     */\n    public PointF mapViewToImage(PointF viewPoint) {\n        float[] points = mTempValues;\n        points[0] = viewPoint.x;\n        points[1] = viewPoint.y;\n        mActiveTransform.invert(mActiveTransformInverse);\n        mActiveTransformInverse.mapPoints(points, 0, points, 0, 1);\n        mapAbsoluteToRelative(points, points, 1);\n        return new PointF(points[0], points[1]);\n    }\n\n    /**\n     * Maps point from image-relative to view-absolute coordinates. This takes into account the\n     * zoomable transformation.\n     */\n    public PointF mapImageToView(PointF imagePoint) {\n        float[] points = mTempValues;\n        points[0] = imagePoint.x;\n        points[1] = imagePoint.y;\n        mapRelativeToAbsolute(points, points, 1);\n        mActiveTransform.mapPoints(points, 0, points, 0, 1);\n        return new PointF(points[0], points[1]);\n    }\n\n    /**\n     * Maps array of 2D points from view-absolute to image-relative coordinates. This does NOT take\n     * into account the zoomable transformation. Points are represented by a float array of [x0, y0,\n     * x1, y1, ...].\n     *\n     * @param destPoints destination array (may be the same as source array)\n     * @param srcPoints  source array\n     * @param numPoints  number of points to map\n     */\n    private void mapAbsoluteToRelative(float[] destPoints, float[] srcPoints, int numPoints) {\n        for (int i = 0; i < numPoints; i++) {\n            destPoints[i * 2 + 0] = (srcPoints[i * 2 + 0] - mImageBounds.left) / mImageBounds.width();\n            destPoints[i * 2 + 1] = (srcPoints[i * 2 + 1] - mImageBounds.top) / mImageBounds.height();\n        }\n    }\n\n    /**\n     * Maps array of 2D points from image-relative to view-absolute coordinates. This does NOT take\n     * into account the zoomable transformation. Points are represented by float array of [x0, y0, x1,\n     * y1, ...].\n     *\n     * @param destPoints destination array (may be the same as source array)\n     * @param srcPoints  source array\n     * @param numPoints  number of points to map\n     */\n    private void mapRelativeToAbsolute(float[] destPoints, float[] srcPoints, int numPoints) {\n        for (int i = 0; i < numPoints; i++) {\n            destPoints[i * 2 + 0] = srcPoints[i * 2 + 0] * mImageBounds.width() + mImageBounds.left;\n            destPoints[i * 2 + 1] = srcPoints[i * 2 + 1] * mImageBounds.height() + mImageBounds.top;\n        }\n    }\n\n    /**\n     * Zooms to the desired scale and positions the image so that the given image point corresponds to\n     * the given view point.\n     *\n     * @param scale      desired scale, will be limited to {min, max} scale factor\n     * @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)\n     * @param viewPoint  2D point in view's absolute coordinate system\n     */\n    public void zoomToPoint(float scale, PointF imagePoint, PointF viewPoint) {\n        FLog.v(TAG, \"zoomToPoint\");\n        calculateZoomToPointTransform(mActiveTransform, scale, imagePoint, viewPoint, LIMIT_ALL);\n        onTransformChanged();\n    }\n\n    /**\n     * Calculates the zoom transformation that would zoom to the desired scale and position the image\n     * so that the given image point corresponds to the given view point.\n     *\n     * @param outTransform the matrix to store the result to\n     * @param scale        desired scale, will be limited to {min, max} scale factor\n     * @param imagePoint   2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)\n     * @param viewPoint    2D point in view's absolute coordinate system\n     * @param limitFlags   whether to limit translation and/or scale.\n     * @return whether or not the transform has been corrected due to limitation\n     */\n    protected boolean calculateZoomToPointTransform(\n            Matrix outTransform,\n            float scale,\n            PointF imagePoint,\n            PointF viewPoint,\n            @LimitFlag int limitFlags) {\n        float[] viewAbsolute = mTempValues;\n        viewAbsolute[0] = imagePoint.x;\n        viewAbsolute[1] = imagePoint.y;\n        mapRelativeToAbsolute(viewAbsolute, viewAbsolute, 1);\n        float distanceX = viewPoint.x - viewAbsolute[0];\n        float distanceY = viewPoint.y - viewAbsolute[1];\n        boolean transformCorrected = false;\n        outTransform.setScale(scale, scale, viewAbsolute[0], viewAbsolute[1]);\n        transformCorrected |= limitScale(outTransform, viewAbsolute[0], viewAbsolute[1], limitFlags);\n        outTransform.postTranslate(distanceX, distanceY);\n        transformCorrected |= limitTranslation(outTransform, limitFlags);\n        return transformCorrected;\n    }\n\n    /**\n     * Sets a new zoom transformation.\n     */\n    public void setTransform(Matrix newTransform) {\n        FLog.v(TAG, \"setTransform\");\n        mActiveTransform.set(newTransform);\n        onTransformChanged();\n    }\n\n    /**\n     * Gets the gesture detector.\n     */\n    protected TransformGestureDetector getDetector() {\n        return mGestureDetector;\n    }\n\n    /**\n     * Notifies controller of the received touch event.\n     */\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        FLog.v(TAG, \"onTouchEvent: action: \", event.getAction());\n        if (mIsEnabled && mIsGestureZoomEnabled) {\n            return mGestureDetector.onTouchEvent(event);\n        }\n        return false;\n    }\n\n    /* TransformGestureDetector.Listener methods  */\n\n    @Override\n    public void onGestureBegin(TransformGestureDetector detector) {\n        FLog.v(TAG, \"onGestureBegin\");\n        mPreviousTransform.set(mActiveTransform);\n        onTransformBegin();\n        // We only received a touch down event so far, and so we don't know yet in which direction a\n        // future move event will follow. Therefore, if we can't scroll in all directions, we have to\n        // assume the worst case where the user tries to scroll out of edge, which would cause\n        // transformation to be corrected.\n        mWasTransformCorrected = !canScrollInAllDirection();\n    }\n\n    @Override\n    public void onGestureUpdate(TransformGestureDetector detector) {\n        FLog.v(TAG, \"onGestureUpdate\");\n        boolean transformCorrected = calculateGestureTransform(mActiveTransform, LIMIT_ALL);\n        onTransformChanged();\n        if (transformCorrected) {\n            mGestureDetector.restartGesture();\n        }\n        // A transformation happened, but was it without correction?\n        mWasTransformCorrected = transformCorrected;\n    }\n\n    @Override\n    public void onGestureEnd(TransformGestureDetector detector) {\n        FLog.v(TAG, \"onGestureEnd\");\n        onTransformEnd();\n    }\n\n    /**\n     * Calculates the zoom transformation based on the current gesture.\n     *\n     * @param outTransform the matrix to store the result to\n     * @param limitTypes   whether to limit translation and/or scale.\n     * @return whether or not the transform has been corrected due to limitation\n     */\n    protected boolean calculateGestureTransform(Matrix outTransform, @LimitFlag int limitTypes) {\n        TransformGestureDetector detector = mGestureDetector;\n        boolean transformCorrected = false;\n        outTransform.set(mPreviousTransform);\n        if (mIsRotationEnabled) {\n            float angle = detector.getRotation() * (float) (180 / Math.PI);\n            outTransform.postRotate(angle, detector.getPivotX(), detector.getPivotY());\n        }\n        if (mIsScaleEnabled) {\n            float scale = detector.getScale();\n            outTransform.postScale(scale, scale, detector.getPivotX(), detector.getPivotY());\n        }\n        transformCorrected |=\n                limitScale(outTransform, detector.getPivotX(), detector.getPivotY(), limitTypes);\n        if (mIsTranslationEnabled) {\n            outTransform.postTranslate(detector.getTranslationX(), detector.getTranslationY());\n        }\n        transformCorrected |= limitTranslation(outTransform, limitTypes);\n        return transformCorrected;\n    }\n\n    private void onTransformBegin() {\n        if (mListener != null && isEnabled()) {\n            mListener.onTransformBegin(mActiveTransform);\n        }\n    }\n\n    private void onTransformChanged() {\n        mActiveTransform.mapRect(mTransformedImageBounds, mImageBounds);\n        if (mListener != null && isEnabled()) {\n            mListener.onTransformChanged(mActiveTransform);\n        }\n    }\n\n    private void onTransformEnd() {\n        if (mListener != null && isEnabled()) {\n            mListener.onTransformEnd(mActiveTransform);\n        }\n    }\n\n    /**\n     * Keeps the scaling factor within the specified limits.\n     *\n     * @param pivotX     x coordinate of the pivot point\n     * @param pivotY     y coordinate of the pivot point\n     * @param limitTypes whether to limit scale.\n     * @return whether limiting has been applied or not\n     */\n    private boolean limitScale(\n            Matrix transform, float pivotX, float pivotY, @LimitFlag int limitTypes) {\n        if (!shouldLimit(limitTypes, LIMIT_SCALE)) {\n            return false;\n        }\n        float currentScale = getMatrixScaleFactor(transform);\n        float targetScale = limit(currentScale, mMinScaleFactor, mMaxScaleFactor);\n        if (targetScale != currentScale) {\n            float scale = targetScale / currentScale;\n            transform.postScale(scale, scale, pivotX, pivotY);\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Limits the translation so that there are no empty spaces on the sides if possible.\n     *\n     * <p>The image is attempted to be centered within the view bounds if the transformed image is\n     * smaller. There will be no empty spaces within the view bounds if the transformed image is\n     * bigger. This applies to each dimension (horizontal and vertical) independently.\n     *\n     * @param limitTypes whether to limit translation along the specific axis.\n     * @return whether limiting has been applied or not\n     */\n    private boolean limitTranslation(Matrix transform, @LimitFlag int limitTypes) {\n        if (!shouldLimit(limitTypes, LIMIT_TRANSLATION_X | LIMIT_TRANSLATION_Y)) {\n            return false;\n        }\n        RectF b = mTempRect;\n        b.set(mImageBounds);\n        transform.mapRect(b);\n        final boolean shouldLimitX = shouldLimit(limitTypes, LIMIT_TRANSLATION_X);\n        float offsetLeft = shouldLimitX\n                           ? getOffset(b.left, b.right, mViewBounds.left, mViewBounds.right, mImageBounds.centerX())\n                           : 0;\n        float offsetTop = shouldLimit(limitTypes, LIMIT_TRANSLATION_Y)\n                          ? getOffset(b.top, b.bottom, mViewBounds.top, mViewBounds.bottom, mImageBounds.centerY())\n                          : 0;\n        if (mListener != null) {\n            mListener.onTranslationLimited(offsetLeft, offsetTop);\n        }\n        if (offsetLeft != 0 || offsetTop != 0) {\n            transform.postTranslate(offsetLeft, offsetTop);\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Checks whether the specified limit flag is present in the limits provided.\n     *\n     * <p>If the flag contains multiple flags together using a bitwise OR, this only checks that at\n     * least one of the flags is included.\n     *\n     * @param limits the limits to apply\n     * @param flag   the limit flag(s) to check for\n     * @return true if the flag (or one of the flags) is included in the limits\n     */\n    private static boolean shouldLimit(@LimitFlag int limits, @LimitFlag int flag) {\n        return (limits & flag) != LIMIT_NONE;\n    }\n\n    /**\n     * Returns the offset necessary to make sure that: - the image is centered within the limit if the\n     * image is smaller than the limit - there is no empty space on left/right if the image is bigger\n     * than the limit\n     */\n    private float getOffset(\n            float imageStart, float imageEnd, float limitStart, float limitEnd, float limitCenter) {\n        float imageWidth = imageEnd - imageStart, limitWidth = limitEnd - limitStart;\n        float limitInnerWidth = Math.min(limitCenter - limitStart, limitEnd - limitCenter) * 2;\n        // center if smaller than limitInnerWidth\n        if (imageWidth < limitInnerWidth) {\n            return limitCenter - (imageEnd + imageStart) / 2;\n        }\n        // to the edge if in between and limitCenter is not (limitLeft + limitRight) / 2\n        if (imageWidth < limitWidth) {\n            if (limitCenter < (limitStart + limitEnd) / 2) {\n                return limitStart - imageStart;\n            } else {\n                return limitEnd - imageEnd;\n            }\n        }\n        // to the edge if larger than limitWidth and empty space visible\n        if (imageStart > limitStart) {\n            return limitStart - imageStart;\n        }\n        if (imageEnd < limitEnd) {\n            return limitEnd - imageEnd;\n        }\n        return 0;\n    }\n\n    /**\n     * Limits the value to the given min and max range.\n     */\n    private float limit(float value, float min, float max) {\n        return Math.min(Math.max(min, value), max);\n    }\n\n    /**\n     * Gets the scale factor for the given matrix. This method assumes the equal scaling factor for X\n     * and Y axis.\n     */\n    private float getMatrixScaleFactor(Matrix transform) {\n        transform.getValues(mTempValues);\n        return mTempValues[Matrix.MSCALE_X];\n    }\n\n    /**\n     * Same as {@code Matrix.isIdentity()}, but with tolerance {@code eps}.\n     */\n    private boolean isMatrixIdentity(Matrix transform, float eps) {\n        // Checks whether the given matrix is close enough to the identity matrix:\n        //   1 0 0\n        //   0 1 0\n        //   0 0 1\n        // Or equivalently to the zero matrix, after subtracting 1.0f from the diagonal elements:\n        //   0 0 0\n        //   0 0 0\n        //   0 0 0\n        transform.getValues(mTempValues);\n        mTempValues[0] -= 1.0f; // m00\n        mTempValues[4] -= 1.0f; // m11\n        mTempValues[8] -= 1.0f; // m22\n        for (int i = 0; i < 9; i++) {\n            if (Math.abs(mTempValues[i]) > eps) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Returns whether the scroll can happen in all directions. I.e. the image is not on any edge.\n     */\n    private boolean canScrollInAllDirection() {\n        return mTransformedImageBounds.left < mViewBounds.left - EPS\n                && mTransformedImageBounds.top < mViewBounds.top - EPS\n                && mTransformedImageBounds.right > mViewBounds.right + EPS\n                && mTransformedImageBounds.bottom > mViewBounds.bottom + EPS;\n    }\n\n    @Override\n    public int computeHorizontalScrollRange() {\n        return (int) mTransformedImageBounds.width();\n    }\n\n    @Override\n    public int computeHorizontalScrollOffset() {\n        return (int) (mViewBounds.left - mTransformedImageBounds.left);\n    }\n\n    @Override\n    public int computeHorizontalScrollExtent() {\n        return (int) mViewBounds.width();\n    }\n\n    @Override\n    public int computeVerticalScrollRange() {\n        return (int) mTransformedImageBounds.height();\n    }\n\n    @Override\n    public int computeVerticalScrollOffset() {\n        return (int) (mViewBounds.top - mTransformedImageBounds.top);\n    }\n\n    @Override\n    public int computeVerticalScrollExtent() {\n        return (int) mViewBounds.height();\n    }\n\n    public Listener getListener() {\n        return mListener;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/DoubleTapGestureListener.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.graphics.PointF;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\n\n/**\n * Tap gesture listener for double tap to zoom / unzoom and double-tap-and-drag to zoom.\n *\n * @see ZoomableDraweeView#setTapListener(GestureDetector.SimpleOnGestureListener)\n */\npublic class DoubleTapGestureListener extends GestureDetector.SimpleOnGestureListener {\n    private static final int DURATION_MS = 300;\n    private static final int DOUBLE_TAP_SCROLL_THRESHOLD = 20;\n\n    private final ZoomableDraweeView mDraweeView;\n    private final PointF mDoubleTapViewPoint = new PointF();\n    private final PointF mDoubleTapImagePoint = new PointF();\n    private float mDoubleTapScale = 1;\n    private boolean mDoubleTapScroll = false;\n\n    public DoubleTapGestureListener(ZoomableDraweeView zoomableDraweeView) {\n        mDraweeView = zoomableDraweeView;\n    }\n\n    @Override\n    public boolean onDoubleTapEvent(MotionEvent e) {\n        AbstractAnimatedZoomableController zc =\n                (AbstractAnimatedZoomableController) mDraweeView.getZoomableController();\n        PointF vp = new PointF(e.getX(), e.getY());\n        PointF ip = zc.mapViewToImage(vp);\n        switch (e.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                mDoubleTapViewPoint.set(vp);\n                mDoubleTapImagePoint.set(ip);\n                mDoubleTapScale = zc.getScaleFactor();\n                break;\n            case MotionEvent.ACTION_MOVE:\n                mDoubleTapScroll = mDoubleTapScroll || shouldStartDoubleTapScroll(vp);\n                if (mDoubleTapScroll) {\n                    float scale = calcScale(vp);\n                    zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);\n                }\n                break;\n            case MotionEvent.ACTION_UP:\n                if (mDoubleTapScroll) {\n                    float scale = calcScale(vp);\n                    zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);\n                } else {\n                    final float maxScale = zc.getMaxScaleFactor();\n                    final float minScale = zc.getMinScaleFactor();\n                    if (zc.getScaleFactor() < (maxScale + minScale) / 2) {\n                        zc.zoomToPoint(\n                                maxScale, ip, vp, DefaultZoomableController.LIMIT_ALL, DURATION_MS, null);\n                    } else {\n                        zc.zoomToPoint(\n                                minScale, ip, vp, DefaultZoomableController.LIMIT_ALL, DURATION_MS, null);\n                    }\n                }\n                mDoubleTapScroll = false;\n                break;\n        }\n        return true;\n    }\n\n    private boolean shouldStartDoubleTapScroll(PointF viewPoint) {\n        double dist =\n                Math.hypot(viewPoint.x - mDoubleTapViewPoint.x, viewPoint.y - mDoubleTapViewPoint.y);\n        return dist > DOUBLE_TAP_SCROLL_THRESHOLD;\n    }\n\n    private float calcScale(PointF currentViewPoint) {\n        float dy = (currentViewPoint.y - mDoubleTapViewPoint.y);\n        float t = 1 + Math.abs(dy) * 0.001f;\n        return (dy < 0) ? mDoubleTapScale / t : mDoubleTapScale * t;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/DraggableZoomableDraweeView.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.NonNull;\n\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\n\nimport awais.instagrabber.customviews.VerticalDragHelper;\nimport awais.instagrabber.customviews.VerticalDragHelper.OnVerticalDragListener;\n\npublic class DraggableZoomableDraweeView extends ZoomableDraweeView {\n    private static final String TAG = \"DraggableZoomableDV\";\n\n    private VerticalDragHelper verticalDragHelper;\n\n    public DraggableZoomableDraweeView(final Context context, final GenericDraweeHierarchy hierarchy) {\n        super(context, hierarchy);\n        verticalDragHelper = new VerticalDragHelper(this);\n    }\n\n    public DraggableZoomableDraweeView(final Context context) {\n        super(context);\n        verticalDragHelper = new VerticalDragHelper(this);\n    }\n\n    public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n        verticalDragHelper = new VerticalDragHelper(this);\n    }\n\n    public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs, final int defStyle) {\n        super(context, attrs, defStyle);\n        verticalDragHelper = new VerticalDragHelper(this);\n    }\n\n    public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {\n        verticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);\n    }\n\n    private int lastPointerCount;\n    private int lastNewPointerCount;\n    private boolean wasTransformCorrected;\n\n    // @Override\n    // protected void onTransformEnd(final Matrix transform) {\n    //     super.onTransformEnd(transform);\n    //     final AnimatedZoomableController zoomableController = (AnimatedZoomableController) getZoomableController();\n    //     final TransformGestureDetector detector = zoomableController.getDetector();\n    //     lastNewPointerCount = detector.getNewPointerCount();\n    //     lastPointerCount = detector.getPointerCount();\n    // }\n    //\n    // @Override\n    // protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {\n    //     super.onTranslationLimited(offsetLeft, offsetTop);\n    //     wasTransformCorrected = offsetTop != 0;\n    // }\n\n    // @SuppressLint(\"ClickableViewAccessibility\")\n    // @Override\n    // public boolean onTouchEvent(final MotionEvent event) {\n    //     boolean superResult = false;\n    //     superResult = super.onTouchEvent(event);\n    // if (verticalDragHelper.isDragging()) {\n    //     final boolean onDragTouch = verticalDragHelper.onDragTouch(event);\n    //     if (onDragTouch) {\n    //         return true;\n    //     }\n    // }\n    // if (!verticalDragHelper.isDragging()) {\n    //     superResult = super.onTouchEvent(event);\n    //     if (wasTransformCorrected\n    //             && (lastPointerCount == 1 || lastPointerCount == 0)\n    //             && (lastNewPointerCount == 1 || lastNewPointerCount == 0)) {\n    //         final boolean onDragTouch = verticalDragHelper.onDragTouch(event);\n    //         if (onDragTouch) {\n    //             return true;\n    //         }\n    //     }\n    // }\n    // final boolean gestureListenerResult = verticalDragHelper.onGestureTouchEvent(event);\n    // if (gestureListenerResult) {\n    //     return true;\n    // }\n    // return superResult;\n    // }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/GestureListenerWrapper.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\n\n/**\n * Wrapper for SimpleOnGestureListener as GestureDetector does not allow changing its listener.\n */\npublic class GestureListenerWrapper extends GestureDetector.SimpleOnGestureListener {\n\n    private GestureDetector.SimpleOnGestureListener mDelegate;\n\n    public GestureListenerWrapper() {\n        mDelegate = new GestureDetector.SimpleOnGestureListener();\n    }\n\n    public void setListener(GestureDetector.SimpleOnGestureListener listener) {\n        mDelegate = listener;\n    }\n\n    @Override\n    public void onLongPress(MotionEvent e) {\n        mDelegate.onLongPress(e);\n    }\n\n    @Override\n    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n        return mDelegate.onScroll(e1, e2, distanceX, distanceY);\n    }\n\n    @Override\n    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n        return mDelegate.onFling(e1, e2, velocityX, velocityY);\n    }\n\n    @Override\n    public void onShowPress(MotionEvent e) {\n        mDelegate.onShowPress(e);\n    }\n\n    @Override\n    public boolean onDown(MotionEvent e) {\n        return mDelegate.onDown(e);\n    }\n\n    @Override\n    public boolean onDoubleTap(MotionEvent e) {\n        return mDelegate.onDoubleTap(e);\n    }\n\n    @Override\n    public boolean onDoubleTapEvent(MotionEvent e) {\n        return mDelegate.onDoubleTapEvent(e);\n    }\n\n    @Override\n    public boolean onSingleTapConfirmed(MotionEvent e) {\n        return mDelegate.onSingleTapConfirmed(e);\n    }\n\n    @Override\n    public boolean onSingleTapUp(MotionEvent e) {\n        return mDelegate.onSingleTapUp(e);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/MultiGestureListener.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Gesture listener that allows multiple child listeners to be added and notified about gesture\n * events.\n *\n * <p>NOTE: The order of the listeners is important. Listeners can consume gesture events. For\n * example, if one of the child listeners consumes {@link #onLongPress(MotionEvent)} (the listener\n * returned true), subsequent listeners will not be notified about the event any more since it has\n * been consumed.\n */\npublic class MultiGestureListener extends GestureDetector.SimpleOnGestureListener {\n\n    private final List<GestureDetector.SimpleOnGestureListener> mListeners = new ArrayList<>();\n\n    /**\n     * Adds a listener to the multi gesture listener.\n     *\n     * <p>NOTE: The order of the listeners is important since gesture events can be consumed.\n     *\n     * @param listener the listener to be added\n     */\n    public synchronized void addListener(GestureDetector.SimpleOnGestureListener listener) {\n        mListeners.add(listener);\n    }\n\n    /**\n     * Removes the given listener so that it will not be notified about future events.\n     *\n     * <p>NOTE: The order of the listeners is important since gesture events can be consumed.\n     *\n     * @param listener the listener to remove\n     */\n    public synchronized void removeListener(GestureDetector.SimpleOnGestureListener listener) {\n        mListeners.remove(listener);\n    }\n\n    @Override\n    public synchronized boolean onSingleTapUp(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onSingleTapUp(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized void onLongPress(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            mListeners.get(i).onLongPress(e);\n        }\n    }\n\n    @Override\n    public synchronized boolean onScroll(\n            MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onScroll(e1, e2, distanceX, distanceY)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean onFling(\n            MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onFling(e1, e2, velocityX, velocityY)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized void onShowPress(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            mListeners.get(i).onShowPress(e);\n        }\n    }\n\n    @Override\n    public synchronized boolean onDown(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onDown(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean onDoubleTap(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onDoubleTap(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean onDoubleTapEvent(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onDoubleTapEvent(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean onSingleTapConfirmed(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onSingleTapConfirmed(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean onContextClick(MotionEvent e) {\n        final int size = mListeners.size();\n        for (int i = 0; i < size; i++) {\n            if (mListeners.get(i).onContextClick(e)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/MultiPointerGestureDetector.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.view.MotionEvent;\n\n/**\n * Component that detects and tracks multiple pointers based on touch events.\n *\n * <p>Each time a pointer gets pressed or released, the current gesture (if any) will end, and a new\n * one will be started (if there are still pressed pointers left). It is guaranteed that the number\n * of pointers within the single gesture will remain the same during the whole gesture.\n */\npublic class MultiPointerGestureDetector {\n\n    /**\n     * The listener for receiving notifications when gestures occur.\n     */\n    public interface Listener {\n        /**\n         * A callback called right before the gesture is about to start.\n         */\n        public void onGestureBegin(MultiPointerGestureDetector detector);\n\n        /**\n         * A callback called each time the gesture gets updated.\n         */\n        public void onGestureUpdate(MultiPointerGestureDetector detector);\n\n        /**\n         * A callback called right after the gesture has finished.\n         */\n        public void onGestureEnd(MultiPointerGestureDetector detector);\n    }\n\n    private static final int MAX_POINTERS = 2;\n\n    private boolean mGestureInProgress;\n    private int mPointerCount;\n    private int mNewPointerCount;\n    private final int mId[] = new int[MAX_POINTERS];\n    private final float mStartX[] = new float[MAX_POINTERS];\n    private final float mStartY[] = new float[MAX_POINTERS];\n    private final float mCurrentX[] = new float[MAX_POINTERS];\n    private final float mCurrentY[] = new float[MAX_POINTERS];\n\n    private Listener mListener = null;\n\n    public MultiPointerGestureDetector() {\n        reset();\n    }\n\n    /**\n     * Factory method that creates a new instance of MultiPointerGestureDetector\n     */\n    public static MultiPointerGestureDetector newInstance() {\n        return new MultiPointerGestureDetector();\n    }\n\n    /**\n     * Sets the listener.\n     *\n     * @param listener listener to set\n     */\n    public void setListener(Listener listener) {\n        mListener = listener;\n    }\n\n    /**\n     * Resets the component to the initial state.\n     */\n    public void reset() {\n        mGestureInProgress = false;\n        mPointerCount = 0;\n        for (int i = 0; i < MAX_POINTERS; i++) {\n            mId[i] = MotionEvent.INVALID_POINTER_ID;\n        }\n    }\n\n    /**\n     * This method can be overridden in order to perform threshold check or something similar.\n     *\n     * @return whether or not to start a new gesture\n     */\n    protected boolean shouldStartGesture() {\n        return true;\n    }\n\n    /**\n     * Starts a new gesture and calls the listener just before starting it.\n     */\n    private void startGesture() {\n        if (!mGestureInProgress) {\n            if (mListener != null) {\n                mListener.onGestureBegin(this);\n            }\n            mGestureInProgress = true;\n        }\n    }\n\n    /**\n     * Stops the current gesture and calls the listener right after stopping it.\n     */\n    private void stopGesture() {\n        if (mGestureInProgress) {\n            mGestureInProgress = false;\n            if (mListener != null) {\n                mListener.onGestureEnd(this);\n            }\n        }\n    }\n\n    /**\n     * Gets the index of the i-th pressed pointer. Normally, the index will be equal to i, except in\n     * the case when the pointer is released.\n     *\n     * @return index of the specified pointer or -1 if not found (i.e. not enough pointers are down)\n     */\n    private int getPressedPointerIndex(MotionEvent event, int i) {\n        final int count = event.getPointerCount();\n        final int action = event.getActionMasked();\n        final int index = event.getActionIndex();\n        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {\n            if (i >= index) {\n                i++;\n            }\n        }\n        return (i < count) ? i : -1;\n    }\n\n    /**\n     * Gets the number of pressed pointers (fingers down).\n     */\n    private static int getPressedPointerCount(MotionEvent event) {\n        int count = event.getPointerCount();\n        int action = event.getActionMasked();\n        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {\n            count--;\n        }\n        return count;\n    }\n\n    private void updatePointersOnTap(MotionEvent event) {\n        mPointerCount = 0;\n        for (int i = 0; i < MAX_POINTERS; i++) {\n            int index = getPressedPointerIndex(event, i);\n            if (index == -1) {\n                mId[i] = MotionEvent.INVALID_POINTER_ID;\n            } else {\n                mId[i] = event.getPointerId(index);\n                mCurrentX[i] = mStartX[i] = event.getX(index);\n                mCurrentY[i] = mStartY[i] = event.getY(index);\n                mPointerCount++;\n            }\n        }\n    }\n\n    private void updatePointersOnMove(MotionEvent event) {\n        for (int i = 0; i < MAX_POINTERS; i++) {\n            int index = event.findPointerIndex(mId[i]);\n            if (index != -1) {\n                mCurrentX[i] = event.getX(index);\n                mCurrentY[i] = event.getY(index);\n            }\n        }\n    }\n\n    /**\n     * Handles the given motion event.\n     *\n     * @param event event to handle\n     * @return whether or not the event was handled\n     */\n    public boolean onTouchEvent(final MotionEvent event) {\n        switch (event.getActionMasked()) {\n            case MotionEvent.ACTION_MOVE: {\n                // update pointers\n                updatePointersOnMove(event);\n                // start a new gesture if not already started\n                if (!mGestureInProgress && mPointerCount > 0 && shouldStartGesture()) {\n                    startGesture();\n                }\n                // notify listener\n                if (mGestureInProgress && mListener != null) {\n                    mListener.onGestureUpdate(this);\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_DOWN:\n            case MotionEvent.ACTION_POINTER_DOWN:\n            case MotionEvent.ACTION_POINTER_UP:\n            case MotionEvent.ACTION_UP: {\n                // restart gesture whenever the number of pointers changes\n                mNewPointerCount = getPressedPointerCount(event);\n                stopGesture();\n                updatePointersOnTap(event);\n                if (mPointerCount > 0 && shouldStartGesture()) {\n                    startGesture();\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL: {\n                mNewPointerCount = 0;\n                stopGesture();\n                reset();\n                break;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Restarts the current gesture (if any).\n     */\n    public void restartGesture() {\n        if (!mGestureInProgress) {\n            return;\n        }\n        stopGesture();\n        for (int i = 0; i < MAX_POINTERS; i++) {\n            mStartX[i] = mCurrentX[i];\n            mStartY[i] = mCurrentY[i];\n        }\n        startGesture();\n    }\n\n    /**\n     * Gets whether there is a gesture in progress\n     */\n    public boolean isGestureInProgress() {\n        return mGestureInProgress;\n    }\n\n    /**\n     * Gets the number of pointers after the current gesture\n     */\n    public int getNewPointerCount() {\n        return mNewPointerCount;\n    }\n\n    /**\n     * Gets the number of pointers in the current gesture\n     */\n    public int getPointerCount() {\n        return mPointerCount;\n    }\n\n    /**\n     * Gets the start X coordinates for the all pointers Mutable array is exposed for performance\n     * reasons and is not to be modified by the callers.\n     */\n    public float[] getStartX() {\n        return mStartX;\n    }\n\n    /**\n     * Gets the start Y coordinates for the all pointers Mutable array is exposed for performance\n     * reasons and is not to be modified by the callers.\n     */\n    public float[] getStartY() {\n        return mStartY;\n    }\n\n    /**\n     * Gets the current X coordinates for the all pointers Mutable array is exposed for performance\n     * reasons and is not to be modified by the callers.\n     */\n    public float[] getCurrentX() {\n        return mCurrentX;\n    }\n\n    /**\n     * Gets the current Y coordinates for the all pointers Mutable array is exposed for performance\n     * reasons and is not to be modified by the callers.\n     */\n    public float[] getCurrentY() {\n        return mCurrentY;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/MultiZoomableControllerListener.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.graphics.Matrix;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * An implementation of {@link ZoomableController.Listener} that allows multiple child listeners to\n * be added and notified about {@link ZoomableController} events.\n */\npublic class MultiZoomableControllerListener implements ZoomableController.Listener {\n\n    private final List<ZoomableController.Listener> mListeners = new ArrayList<>();\n\n    @Override\n    public synchronized void onTransformBegin(Matrix transform) {\n        for (ZoomableController.Listener listener : mListeners) {\n            listener.onTransformBegin(transform);\n        }\n    }\n\n    @Override\n    public synchronized void onTransformChanged(Matrix transform) {\n        for (ZoomableController.Listener listener : mListeners) {\n            listener.onTransformChanged(transform);\n        }\n    }\n\n    @Override\n    public synchronized void onTransformEnd(Matrix transform) {\n        for (ZoomableController.Listener listener : mListeners) {\n            listener.onTransformEnd(transform);\n        }\n    }\n\n    @Override\n    public void onTranslationLimited(final float offsetLeft, final float offsetTop) {\n        for (ZoomableController.Listener listener : mListeners) {\n            listener.onTranslationLimited(offsetLeft, offsetTop);\n        }\n    }\n\n    public synchronized void addListener(ZoomableController.Listener listener) {\n        mListeners.add(listener);\n    }\n\n    public synchronized void removeListener(ZoomableController.Listener listener) {\n        mListeners.remove(listener);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/TransformGestureDetector.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.view.MotionEvent;\n\n/**\n * Component that detects translation, scale and rotation based on touch events.\n *\n * <p>This class notifies its listeners whenever a gesture begins, updates or ends. The instance of\n * this detector is passed to the listeners, so it can be queried for pivot, translation, scale or\n * rotation.\n */\npublic class TransformGestureDetector implements MultiPointerGestureDetector.Listener {\n\n    /**\n     * The listener for receiving notifications when gestures occur.\n     */\n    public interface Listener {\n        /**\n         * A callback called right before the gesture is about to start.\n         */\n        public void onGestureBegin(TransformGestureDetector detector);\n\n        /**\n         * A callback called each time the gesture gets updated.\n         */\n        public void onGestureUpdate(TransformGestureDetector detector);\n\n        /**\n         * A callback called right after the gesture has finished.\n         */\n        public void onGestureEnd(TransformGestureDetector detector);\n    }\n\n    private final MultiPointerGestureDetector mDetector;\n\n    private Listener mListener = null;\n\n    public TransformGestureDetector(MultiPointerGestureDetector multiPointerGestureDetector) {\n        mDetector = multiPointerGestureDetector;\n        mDetector.setListener(this);\n    }\n\n    /**\n     * Factory method that creates a new instance of TransformGestureDetector\n     */\n    public static TransformGestureDetector newInstance() {\n        return new TransformGestureDetector(MultiPointerGestureDetector.newInstance());\n    }\n\n    /**\n     * Sets the listener.\n     *\n     * @param listener listener to set\n     */\n    public void setListener(Listener listener) {\n        mListener = listener;\n    }\n\n    /**\n     * Resets the component to the initial state.\n     */\n    public void reset() {\n        mDetector.reset();\n    }\n\n    /**\n     * Handles the given motion event.\n     *\n     * @param event event to handle\n     * @return whether or not the event was handled\n     */\n    public boolean onTouchEvent(final MotionEvent event) {\n        return mDetector.onTouchEvent(event);\n    }\n\n    @Override\n    public void onGestureBegin(MultiPointerGestureDetector detector) {\n        if (mListener != null) {\n            mListener.onGestureBegin(this);\n        }\n    }\n\n    @Override\n    public void onGestureUpdate(MultiPointerGestureDetector detector) {\n        if (mListener != null) {\n            mListener.onGestureUpdate(this);\n        }\n    }\n\n    @Override\n    public void onGestureEnd(MultiPointerGestureDetector detector) {\n        if (mListener != null) {\n            mListener.onGestureEnd(this);\n        }\n    }\n\n    private float calcAverage(float[] arr, int len) {\n        float sum = 0;\n        for (int i = 0; i < len; i++) {\n            sum += arr[i];\n        }\n        return (len > 0) ? sum / len : 0;\n    }\n\n    /**\n     * Restarts the current gesture (if any).\n     */\n    public void restartGesture() {\n        mDetector.restartGesture();\n    }\n\n    /**\n     * Gets whether there is a gesture in progress\n     */\n    public boolean isGestureInProgress() {\n        return mDetector.isGestureInProgress();\n    }\n\n    /**\n     * Gets the number of pointers after the current gesture\n     */\n    public int getNewPointerCount() {\n        return mDetector.getNewPointerCount();\n    }\n\n    /**\n     * Gets the number of pointers in the current gesture\n     */\n    public int getPointerCount() {\n        return mDetector.getPointerCount();\n    }\n\n    /**\n     * Gets the X coordinate of the pivot point\n     */\n    public float getPivotX() {\n        return calcAverage(mDetector.getStartX(), mDetector.getPointerCount());\n    }\n\n    /**\n     * Gets the Y coordinate of the pivot point\n     */\n    public float getPivotY() {\n        return calcAverage(mDetector.getStartY(), mDetector.getPointerCount());\n    }\n\n    /**\n     * Gets the X component of the translation\n     */\n    public float getTranslationX() {\n        return calcAverage(mDetector.getCurrentX(), mDetector.getPointerCount())\n                - calcAverage(mDetector.getStartX(), mDetector.getPointerCount());\n    }\n\n    /**\n     * Gets the Y component of the translation\n     */\n    public float getTranslationY() {\n        return calcAverage(mDetector.getCurrentY(), mDetector.getPointerCount())\n                - calcAverage(mDetector.getStartY(), mDetector.getPointerCount());\n    }\n\n    /**\n     * Gets the scale\n     */\n    public float getScale() {\n        if (mDetector.getPointerCount() < 2) {\n            return 1;\n        } else {\n            float startDeltaX = mDetector.getStartX()[1] - mDetector.getStartX()[0];\n            float startDeltaY = mDetector.getStartY()[1] - mDetector.getStartY()[0];\n            float currentDeltaX = mDetector.getCurrentX()[1] - mDetector.getCurrentX()[0];\n            float currentDeltaY = mDetector.getCurrentY()[1] - mDetector.getCurrentY()[0];\n            float startDist = (float) Math.hypot(startDeltaX, startDeltaY);\n            float currentDist = (float) Math.hypot(currentDeltaX, currentDeltaY);\n            return currentDist / startDist;\n        }\n    }\n\n    /**\n     * Gets the rotation in radians\n     */\n    public float getRotation() {\n        if (mDetector.getPointerCount() < 2) {\n            return 0;\n        } else {\n            float startDeltaX = mDetector.getStartX()[1] - mDetector.getStartX()[0];\n            float startDeltaY = mDetector.getStartY()[1] - mDetector.getStartY()[0];\n            float currentDeltaX = mDetector.getCurrentX()[1] - mDetector.getCurrentX()[0];\n            float currentDeltaY = mDetector.getCurrentY()[1] - mDetector.getCurrentY()[0];\n            float startAngle = (float) Math.atan2(startDeltaY, startDeltaX);\n            float currentAngle = (float) Math.atan2(currentDeltaY, currentDeltaX);\n            return currentAngle - startAngle;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableController.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.view.MotionEvent;\n\n/**\n * Interface for implementing a controller that works with {@link ZoomableDraweeView} to control the\n * zoom.\n */\npublic interface ZoomableController {\n\n    /**\n     * Listener interface.\n     */\n    interface Listener {\n\n        /**\n         * Notifies the view that the transform began.\n         *\n         * @param transform the current transform matrix\n         */\n        void onTransformBegin(Matrix transform);\n\n        /**\n         * Notifies the view that the transform changed.\n         *\n         * @param transform the new matrix\n         */\n        void onTransformChanged(Matrix transform);\n\n        /**\n         * Notifies the view that the transform ended.\n         *\n         * @param transform the current transform matrix\n         */\n        void onTransformEnd(Matrix transform);\n\n        void onTranslationLimited(float offsetLeft, float offsetTop);\n    }\n\n    /**\n     * Enables the controller. The controller is enabled when the image has been loaded.\n     *\n     * @param enabled whether to enable the controller\n     */\n    void setEnabled(boolean enabled);\n\n    /**\n     * Gets whether the controller is enabled. This should return the last value passed to {@link\n     * #setEnabled}.\n     *\n     * @return whether the controller is enabled.\n     */\n    boolean isEnabled();\n\n    /**\n     * Sets the listener for the controller to call back when the matrix changes.\n     *\n     * @param listener the listener\n     */\n    void setListener(Listener listener);\n\n    /**\n     * Gets the current scale factor. A convenience method for calculating the scale from the\n     * transform.\n     *\n     * @return the current scale factor\n     */\n    float getScaleFactor();\n\n    /**\n     * Returns true if the zoomable transform is identity matrix, and the controller is idle.\n     */\n    boolean isIdentity();\n\n    /**\n     * Returns true if the transform was corrected during the last update.\n     *\n     * <p>This mainly happens when a gesture would cause the image to get out of limits and the\n     * transform gets corrected in order to prevent that.\n     */\n    boolean wasTransformCorrected();\n\n    /**\n     * See {@link androidx.core.view.ScrollingView}.\n     */\n    int computeHorizontalScrollRange();\n\n    int computeHorizontalScrollOffset();\n\n    int computeHorizontalScrollExtent();\n\n    int computeVerticalScrollRange();\n\n    int computeVerticalScrollOffset();\n\n    int computeVerticalScrollExtent();\n\n    /**\n     * Gets the current transform.\n     *\n     * @return the transform\n     */\n    Matrix getTransform();\n\n    /**\n     * Sets the bounds of the image post transform prior to application of the zoomable\n     * transformation.\n     *\n     * @param imageBounds the bounds of the image\n     */\n    void setImageBounds(RectF imageBounds);\n\n    /**\n     * Sets the bounds of the view.\n     *\n     * @param viewBounds the bounds of the view\n     */\n    void setViewBounds(RectF viewBounds);\n\n    /**\n     * Allows the controller to handle a touch event.\n     *\n     * @param event the touch event\n     * @return whether the controller handled the event\n     */\n    boolean onTouchEvent(MotionEvent event);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\npackage awais.instagrabber.customviews.drawee;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Animatable;\nimport android.util.AttributeSet;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ViewParent;\n\nimport androidx.annotation.Nullable;\nimport androidx.core.view.ScrollingView;\n\nimport com.facebook.common.internal.Preconditions;\nimport com.facebook.common.logging.FLog;\nimport com.facebook.drawee.controller.AbstractDraweeController;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.drawee.controller.ControllerListener;\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyInflater;\nimport com.facebook.drawee.interfaces.DraweeController;\nimport com.facebook.drawee.view.DraweeView;\n\n\n/**\n * DraweeView that has zoomable capabilities.\n *\n * <p>Once the image loads, pinch-to-zoom and translation gestures are enabled.\n */\npublic class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>\n        implements ScrollingView {\n\n    private static final Class<?> TAG = ZoomableDraweeView.class;\n\n    private static final float HUGE_IMAGE_SCALE_FACTOR_THRESHOLD = 1.1f;\n\n    private final RectF mImageBounds = new RectF();\n    private final RectF mViewBounds = new RectF();\n\n    private DraweeController mHugeImageController;\n    private ZoomableController mZoomableController;\n    private GestureDetector mTapGestureDetector;\n    private boolean mAllowTouchInterceptionWhileZoomed = false;\n\n    private boolean mIsDialtoneEnabled = false;\n    private boolean mZoomingEnabled = true;\n\n    private final ControllerListener mControllerListener =\n            new BaseControllerListener<Object>() {\n                @Override\n                public void onFinalImageSet(\n                        String id, @Nullable Object imageInfo, @Nullable Animatable animatable) {\n                    ZoomableDraweeView.this.onFinalImageSet();\n                }\n\n                @Override\n                public void onRelease(String id) {\n                    ZoomableDraweeView.this.onRelease();\n                }\n            };\n\n    private final ZoomableController.Listener mZoomableListener =\n            new ZoomableController.Listener() {\n                @Override\n                public void onTransformBegin(Matrix transform) {\n                    ZoomableDraweeView.this.onTransformBegin(transform);\n                }\n\n                @Override\n                public void onTransformChanged(Matrix transform) {\n                    ZoomableDraweeView.this.onTransformChanged(transform);\n                }\n\n                @Override\n                public void onTransformEnd(Matrix transform) {\n                    ZoomableDraweeView.this.onTransformEnd(transform);\n                }\n\n                @Override\n                public void onTranslationLimited(final float offsetLeft, final float offsetTop) {\n                    ZoomableDraweeView.this.onTranslationLimited(offsetLeft, offsetTop);\n                }\n            };\n\n    private final GestureListenerWrapper mTapListenerWrapper = new GestureListenerWrapper();\n\n    public ZoomableDraweeView(Context context, GenericDraweeHierarchy hierarchy) {\n        super(context);\n        setHierarchy(hierarchy);\n        init();\n    }\n\n    public ZoomableDraweeView(Context context) {\n        super(context);\n        inflateHierarchy(context, null);\n        init();\n    }\n\n    public ZoomableDraweeView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        inflateHierarchy(context, attrs);\n        init();\n    }\n\n    public ZoomableDraweeView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        inflateHierarchy(context, attrs);\n        init();\n    }\n\n    protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {\n        Resources resources = context.getResources();\n        GenericDraweeHierarchyBuilder builder =\n                new GenericDraweeHierarchyBuilder(resources)\n                        .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);\n        GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);\n        setAspectRatio(builder.getDesiredAspectRatio());\n        setHierarchy(builder.build());\n    }\n\n    private void init() {\n        mZoomableController = createZoomableController();\n        mZoomableController.setListener(mZoomableListener);\n        mTapGestureDetector = new GestureDetector(getContext(), mTapListenerWrapper);\n    }\n\n    public void setIsDialtoneEnabled(boolean isDialtoneEnabled) {\n        mIsDialtoneEnabled = isDialtoneEnabled;\n    }\n\n    /**\n     * Gets the original image bounds, in view-absolute coordinates.\n     *\n     * <p>The original image bounds are those reported by the hierarchy. The hierarchy itself may\n     * apply scaling on its own (e.g. due to scale type) so the reported bounds are not necessarily\n     * the same as the actual bitmap dimensions. In other words, the original image bounds correspond\n     * to the image bounds within this view when no zoomable transformation is applied, but including\n     * the potential scaling of the hierarchy. Having the actual bitmap dimensions abstracted away\n     * from this view greatly simplifies implementation because the actual bitmap may change (e.g.\n     * when a high-res image arrives and replaces the previously set low-res image). With proper\n     * hierarchy scaling (e.g. FIT_CENTER), this underlying change will not affect this view nor the\n     * zoomable transformation in any way.\n     */\n    protected void getImageBounds(RectF outBounds) {\n        getHierarchy().getActualImageBounds(outBounds);\n    }\n\n    /**\n     * Gets the bounds used to limit the translation, in view-absolute coordinates.\n     *\n     * <p>These bounds are passed to the zoomable controller in order to limit the translation. The\n     * image is attempted to be centered within the limit bounds if the transformed image is smaller.\n     * There will be no empty spaces within the limit bounds if the transformed image is bigger. This\n     * applies to each dimension (horizontal and vertical) independently.\n     *\n     * <p>Unless overridden by a subclass, these bounds are same as the view bounds.\n     */\n    protected void getLimitBounds(RectF outBounds) {\n        outBounds.set(0, 0, getWidth(), getHeight());\n    }\n\n    /**\n     * Sets a custom zoomable controller, instead of using the default one.\n     */\n    public void setZoomableController(ZoomableController zoomableController) {\n        Preconditions.checkNotNull(zoomableController);\n        mZoomableController.setListener(null);\n        mZoomableController = zoomableController;\n        mZoomableController.setListener(mZoomableListener);\n    }\n\n    /**\n     * Gets the zoomable controller.\n     *\n     * <p>Zoomable controller can be used to zoom to point, or to map point from view to image\n     * coordinates for instance.\n     */\n    public ZoomableController getZoomableController() {\n        return mZoomableController;\n    }\n\n    /**\n     * Check whether the parent view can intercept touch events while zoomed. This can be used, for\n     * example, to swipe between images in a view pager while zoomed.\n     *\n     * @return true if touch events can be intercepted\n     */\n    public boolean allowsTouchInterceptionWhileZoomed() {\n        return mAllowTouchInterceptionWhileZoomed;\n    }\n\n    /**\n     * If this is set to true, parent views can intercept touch events while the view is zoomed. For\n     * example, this can be used to swipe between images in a view pager while zoomed.\n     *\n     * @param allowTouchInterceptionWhileZoomed true if the parent needs to intercept touches\n     */\n    public void setAllowTouchInterceptionWhileZoomed(boolean allowTouchInterceptionWhileZoomed) {\n        mAllowTouchInterceptionWhileZoomed = allowTouchInterceptionWhileZoomed;\n    }\n\n    /**\n     * Sets the tap listener.\n     */\n    public void setTapListener(GestureDetector.SimpleOnGestureListener tapListener) {\n        mTapListenerWrapper.setListener(tapListener);\n    }\n\n    /**\n     * Sets whether long-press tap detection is enabled. Unfortunately, long-press conflicts with\n     * onDoubleTapEvent.\n     */\n    public void setIsLongpressEnabled(boolean enabled) {\n        mTapGestureDetector.setIsLongpressEnabled(enabled);\n    }\n\n    public void setZoomingEnabled(boolean zoomingEnabled) {\n        mZoomingEnabled = zoomingEnabled;\n        mZoomableController.setEnabled(zoomingEnabled);\n    }\n\n    /**\n     * Sets the image controller.\n     */\n    @Override\n    public void setController(@Nullable DraweeController controller) {\n        setControllers(controller, null);\n    }\n\n    /**\n     * Sets the controllers for the normal and huge image.\n     *\n     * <p>The huge image controller is used after the image gets scaled above a certain threshold.\n     *\n     * <p>IMPORTANT: in order to avoid a flicker when switching to the huge image, the huge image\n     * controller should have the normal-image-uri set as its low-res-uri.\n     *\n     * @param controller          controller to be initially used\n     * @param hugeImageController controller to be used after the client starts zooming-in\n     */\n    public void setControllers(\n            @Nullable DraweeController controller, @Nullable DraweeController hugeImageController) {\n        setControllersInternal(null, null);\n        mZoomableController.setEnabled(false);\n        setControllersInternal(controller, hugeImageController);\n    }\n\n    private void setControllersInternal(\n            @Nullable DraweeController controller, @Nullable DraweeController hugeImageController) {\n        removeControllerListener(getController());\n        addControllerListener(controller);\n        mHugeImageController = hugeImageController;\n        super.setController(controller);\n    }\n\n    private void maybeSetHugeImageController() {\n        if (mHugeImageController != null\n                && mZoomableController.getScaleFactor() > HUGE_IMAGE_SCALE_FACTOR_THRESHOLD) {\n            setControllersInternal(mHugeImageController, null);\n        }\n    }\n\n    private void removeControllerListener(DraweeController controller) {\n        if (controller instanceof AbstractDraweeController) {\n            ((AbstractDraweeController) controller).removeControllerListener(mControllerListener);\n        }\n    }\n\n    private void addControllerListener(DraweeController controller) {\n        if (controller instanceof AbstractDraweeController) {\n            ((AbstractDraweeController) controller).addControllerListener(mControllerListener);\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        int saveCount = canvas.save();\n        canvas.concat(mZoomableController.getTransform());\n        try {\n            super.onDraw(canvas);\n        } catch (Exception e) {\n            DraweeController controller = getController();\n            if (controller != null && controller instanceof AbstractDraweeController) {\n                Object callerContext = ((AbstractDraweeController) controller).getCallerContext();\n                if (callerContext != null) {\n                    throw new RuntimeException(\n                            String.format(\"Exception in onDraw, callerContext=%s\", callerContext.toString()), e);\n                }\n            }\n            throw e;\n        }\n        canvas.restoreToCount(saveCount);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        int a = event.getActionMasked();\n        FLog.v(getLogTag(), \"onTouchEvent: %d, view %x, received\", a, this.hashCode());\n        if (!mIsDialtoneEnabled && mTapGestureDetector.onTouchEvent(event)) {\n            FLog.v(getLogTag(),\n                   \"onTouchEvent: %d, view %x, handled by tap gesture detector\",\n                   a,\n                   this.hashCode());\n            return true;\n        }\n\n        if (!mIsDialtoneEnabled && mZoomableController.onTouchEvent(event)) {\n            FLog.v(\n                    getLogTag(),\n                    \"onTouchEvent: %d, view %x, handled by zoomable controller\",\n                    a,\n                    this.hashCode());\n            if (!mAllowTouchInterceptionWhileZoomed && !mZoomableController.isIdentity()) {\n                final ViewParent parent = getParent();\n                parent.requestDisallowInterceptTouchEvent(true);\n            }\n            return true;\n        }\n        if (super.onTouchEvent(event)) {\n            FLog.v(getLogTag(), \"onTouchEvent: %d, view %x, handled by the super\", a, this.hashCode());\n            return true;\n        }\n        // None of our components reported that they handled the touch event. Upon returning false\n        // from this method, our parent won't send us any more events for this gesture. Unfortunately,\n        // some components may have started a delayed action, such as a long-press timer, and since we\n        // won't receive an ACTION_UP that would cancel that timer, a false event may be triggered.\n        // To prevent that we explicitly send one last cancel event when returning false.\n        MotionEvent cancelEvent = MotionEvent.obtain(event);\n        cancelEvent.setAction(MotionEvent.ACTION_CANCEL);\n        mTapGestureDetector.onTouchEvent(cancelEvent);\n        mZoomableController.onTouchEvent(cancelEvent);\n        cancelEvent.recycle();\n        return false;\n    }\n\n    @Override\n    public int computeHorizontalScrollRange() {\n        return mZoomableController.computeHorizontalScrollRange();\n    }\n\n    @Override\n    public int computeHorizontalScrollOffset() {\n        return mZoomableController.computeHorizontalScrollOffset();\n    }\n\n    @Override\n    public int computeHorizontalScrollExtent() {\n        return mZoomableController.computeHorizontalScrollExtent();\n    }\n\n    @Override\n    public int computeVerticalScrollRange() {\n        return mZoomableController.computeVerticalScrollRange();\n    }\n\n    @Override\n    public int computeVerticalScrollOffset() {\n        return mZoomableController.computeVerticalScrollOffset();\n    }\n\n    @Override\n    public int computeVerticalScrollExtent() {\n        return mZoomableController.computeVerticalScrollExtent();\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        FLog.v(getLogTag(), \"onLayout: view %x\", this.hashCode());\n        super.onLayout(changed, left, top, right, bottom);\n        updateZoomableControllerBounds();\n    }\n\n    private void onFinalImageSet() {\n        FLog.v(getLogTag(), \"onFinalImageSet: view %x\", this.hashCode());\n        if (!mZoomableController.isEnabled() && mZoomingEnabled) {\n            mZoomableController.setEnabled(true);\n            updateZoomableControllerBounds();\n        }\n    }\n\n    private void onRelease() {\n        FLog.v(getLogTag(), \"onRelease: view %x\", this.hashCode());\n        mZoomableController.setEnabled(false);\n    }\n\n    protected void onTransformBegin(final Matrix transform) {}\n\n    protected void onTransformChanged(Matrix transform) {\n        FLog.v(getLogTag(), \"onTransformChanged: view %x, transform: %s\", this.hashCode(), transform);\n        maybeSetHugeImageController();\n        invalidate();\n    }\n\n    protected void onTransformEnd(final Matrix transform) {}\n\n    protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {}\n\n    protected void updateZoomableControllerBounds() {\n        getImageBounds(mImageBounds);\n        getLimitBounds(mViewBounds);\n        // Log.d(TAG.getSimpleName(), \"updateZoomableControllerBounds: mImageBounds: \" + mImageBounds);\n        mZoomableController.setImageBounds(mImageBounds);\n        mZoomableController.setViewBounds(mViewBounds);\n        FLog.v(getLogTag(),\n               \"updateZoomableControllerBounds: view %x, view bounds: %s, image bounds: %s\",\n               this.hashCode(),\n               mViewBounds,\n               mImageBounds);\n    }\n\n    protected Class<?> getLogTag() {\n        return TAG;\n    }\n\n    protected ZoomableController createZoomableController() {\n        return AnimatedZoomableController.newInstance();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Emoji {\n    private final String unicode;\n    private final String name;\n    private final List<Emoji> variants;\n    private GoogleCompatEmojiDrawable drawable;\n\n    public Emoji(final String unicode,\n                 final String name,\n                 final List<Emoji> variants) {\n        this.unicode = unicode;\n        this.name = name;\n        this.variants = variants;\n    }\n\n    public String getUnicode() {\n        return unicode;\n    }\n\n    public void addVariant(final Emoji emoji) {\n        variants.add(emoji);\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public List<Emoji> getVariants() {\n        return variants;\n    }\n\n    public GoogleCompatEmojiDrawable getDrawable() {\n        if (drawable == null && unicode != null) {\n            drawable = new GoogleCompatEmojiDrawable(unicode);\n        }\n        return drawable;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final Emoji emoji = (Emoji) o;\n        return Objects.equals(unicode, emoji.unicode);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(unicode);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"Emoji{\" +\n                \"unicode='\" + unicode + '\\'' +\n                \", name='\" + name + '\\'' +\n                \", variants=\" + variants +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.bottomsheet.BottomSheetDialog;\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\npublic class EmojiBottomSheetDialog extends BottomSheetDialogFragment {\n    public static final String TAG = EmojiBottomSheetDialog.class.getSimpleName();\n\n    private RecyclerView grid;\n    private EmojiPicker.OnEmojiClickListener callback;\n\n    @NonNull\n    public static EmojiBottomSheetDialog newInstance() {\n        // Bundle args = new Bundle();\n        // fragment.setArguments(args);\n        return new EmojiBottomSheetDialog();\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        final Context context = getContext();\n        if (context == null) return null;\n        grid = new RecyclerView(context);\n        return grid;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;\n        final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);\n        if (bottomSheetInternal == null) return;\n        bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;\n        bottomSheetInternal.requestLayout();\n    }\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        final Fragment parentFragment = getParentFragment();\n        if (parentFragment instanceof EmojiPicker.OnEmojiClickListener) {\n            callback = (EmojiPicker.OnEmojiClickListener) parentFragment;\n        }\n    }\n\n    @Override\n    public void onDestroyView() {\n        grid = null;\n        super.onDestroyView();\n    }\n\n    private void init() {\n        final Context context = getContext();\n        if (context == null) return;\n        final GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 9);\n        grid.setLayoutManager(gridLayoutManager);\n        grid.setHasFixedSize(true);\n        grid.setClipToPadding(false);\n        grid.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(8)));\n        final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context);\n        final EmojiGridAdapter adapter = new EmojiGridAdapter(emojiParser, null, (view, emoji) -> {\n            if (callback != null) {\n                callback.onClick(view, emoji);\n            }\n            dismiss();\n        }, null);\n        grid.setAdapter(adapter);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.NonNull;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport awais.instagrabber.R;\n\npublic class EmojiCategory {\n    private final EmojiCategoryType type;\n    private final Map<String, Emoji> emojis;\n    @DrawableRes\n    private int drawableRes;\n\n    public EmojiCategory(final EmojiCategoryType type, final Map<String, Emoji> emojis) {\n        this.type = type;\n        this.emojis = emojis;\n    }\n\n    public EmojiCategoryType getType() {\n        return type;\n    }\n\n    public Map<String, Emoji> getEmojis() {\n        return emojis;\n    }\n\n    public int getDrawableRes() {\n        if (drawableRes == 0) {\n            switch (type) {\n                case SMILEYS_AND_EMOTION:\n                    drawableRes = R.drawable.ic_round_emoji_emotions_24;\n                    break;\n                case ANIMALS_AND_NATURE:\n                    drawableRes = R.drawable.ic_round_emoji_nature_24;\n                    break;\n                case FOOD_AND_DRINK:\n                    drawableRes = R.drawable.ic_round_emoji_food_beverage_24;\n                    break;\n                case TRAVEL_AND_PLACES:\n                    drawableRes = R.drawable.ic_round_emoji_transportation_24;\n                    break;\n                case ACTIVITIES:\n                    drawableRes = R.drawable.ic_round_emoji_events_24;\n                    break;\n                case OBJECTS:\n                    drawableRes = R.drawable.ic_round_emoji_objects_24;\n                    break;\n                case SYMBOLS:\n                    drawableRes = R.drawable.ic_round_emoji_symbols_24;\n                    break;\n                case FLAGS:\n                    drawableRes = R.drawable.ic_round_emoji_flags_24;\n                    break;\n                case OTHERS:\n                    drawableRes = R.drawable.ic_round_unknown_24;\n                    break;\n            }\n        }\n        return drawableRes;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final EmojiCategory that = (EmojiCategory) o;\n        return type == that.type;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(type);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"EmojiCategory{\" +\n                \"type=\" + type +\n                \", emojis=\" + emojis +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryPageViewHolder.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\npublic class EmojiCategoryPageViewHolder extends RecyclerView.ViewHolder {\n    // private static final String TAG = EmojiCategoryPageViewHolder.class.getSimpleName();\n\n    private final View rootView;\n    private final OnEmojiClickListener onEmojiClickListener;\n    private final EmojiParser emojiParser = EmojiParser.Companion.getInstance(itemView.getContext());\n\n    public EmojiCategoryPageViewHolder(@NonNull final View rootView,\n                                       @NonNull final RecyclerView itemView,\n                                       final OnEmojiClickListener onEmojiClickListener) {\n        super(itemView);\n        this.rootView = rootView;\n        this.onEmojiClickListener = onEmojiClickListener;\n    }\n\n    public void bind(final EmojiCategory emojiCategory) {\n        final RecyclerView emojiGrid = (RecyclerView) itemView;\n        final EmojiGridAdapter adapter = new EmojiGridAdapter(\n                emojiParser,\n                emojiCategory.getType(),\n                onEmojiClickListener,\n                (position, view, parent) -> {\n                    final EmojiVariantPopup emojiVariantPopup = new EmojiVariantPopup(rootView, ((view1, emoji) -> {\n                        if (onEmojiClickListener != null) {\n                            onEmojiClickListener.onClick(view1, emoji);\n                        }\n                        final EmojiGridAdapter emojiGridAdapter = (EmojiGridAdapter) emojiGrid.getAdapter();\n                        if (emojiGridAdapter == null) return;\n                        emojiGridAdapter.notifyItemChanged(position);\n                    }));\n                    emojiVariantPopup.show(view, parent);\n                    return true;\n                }\n        );\n        emojiGrid.setAdapter(adapter);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategoryType.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic enum EmojiCategoryType {\n    SMILEYS_AND_EMOTION(\"Smileys & Emotion\"),\n    // PEOPLE_AND_BODY(\"People & Body\"),\n    ANIMALS_AND_NATURE(\"Animals & Nature\"),\n    FOOD_AND_DRINK(\"Food & Drink\"),\n    TRAVEL_AND_PLACES(\"Travel & Places\"),\n    ACTIVITIES(\"Activities\"),\n    OBJECTS(\"Objects\"),\n    SYMBOLS(\"Symbols\"),\n    FLAGS(\"Flags\"),\n    OTHERS(\"Others\");\n\n    private final String name;\n\n    private static final Map<String, EmojiCategoryType> map = new HashMap<>();\n\n    static {\n        for (EmojiCategoryType type : EmojiCategoryType.values()) {\n            map.put(type.name, type);\n        }\n    }\n\n    EmojiCategoryType(final String name) {\n        this.name = name;\n    }\n\n    public static EmojiCategoryType valueOfName(final String name) {\n        final EmojiCategoryType emojiCategoryType = map.get(name);\n        if (emojiCategoryType == null) {\n            return EmojiCategoryType.OTHERS;\n        }\n        return emojiCategoryType;\n    }\n\n    public String getName() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.AdapterListUpdateCallback;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.AsyncListDiffer;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener;\nimport awais.instagrabber.databinding.ItemEmojiGridBinding;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\npublic class EmojiGridAdapter extends RecyclerView.Adapter<EmojiGridAdapter.EmojiViewHolder> {\n    private static final String TAG = EmojiGridAdapter.class.getSimpleName();\n\n    private static final DiffUtil.ItemCallback<Emoji> diffCallback = new DiffUtil.ItemCallback<Emoji>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final Emoji oldItem, @NonNull final Emoji newItem) {\n            return oldItem.equals(newItem);\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final Emoji oldItem, @NonNull final Emoji newItem) {\n            return oldItem.equals(newItem);\n        }\n    };\n\n    private final AsyncListDiffer<Emoji> differ;\n    private final OnEmojiLongClickListener onEmojiLongClickListener;\n    private final OnEmojiClickListener onEmojiClickListener;\n    private final EmojiVariantManager emojiVariantManager;\n    private final AppExecutors appExecutors;\n\n    public EmojiGridAdapter(@NonNull final EmojiParser emojiParser,\n                            final EmojiCategoryType emojiCategoryType,\n                            final OnEmojiClickListener onEmojiClickListener,\n                            final OnEmojiLongClickListener onEmojiLongClickListener) {\n        this.onEmojiClickListener = onEmojiClickListener;\n        this.onEmojiLongClickListener = onEmojiLongClickListener;\n        differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),\n                                       new AsyncDifferConfig.Builder<>(diffCallback).build());\n        final Map<EmojiCategoryType, EmojiCategory> categoryMap = emojiParser.getCategoryMap();\n        emojiVariantManager = EmojiVariantManager.getInstance();\n        appExecutors = AppExecutors.INSTANCE;\n        setHasStableIds(true);\n        if (emojiCategoryType == null) {\n            // show all if type is null\n            differ.submitList(ImmutableList.copyOf(emojiParser.getAllEmojis().values()));\n            return;\n        }\n        final EmojiCategory emojiCategory = categoryMap.get(emojiCategoryType);\n        if (emojiCategory == null) {\n            differ.submitList(Collections.emptyList());\n            return;\n        }\n        differ.submitList(ImmutableList.copyOf(emojiCategory.getEmojis().values()));\n    }\n\n    @NonNull\n    @Override\n    public EmojiViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());\n        final ItemEmojiGridBinding binding = ItemEmojiGridBinding.inflate(layoutInflater, parent, false);\n        return new EmojiViewHolder(binding, onEmojiClickListener, onEmojiLongClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final EmojiViewHolder holder, final int position) {\n        final Emoji emoji = differ.getCurrentList().get(position);\n        final String variant = emojiVariantManager.getVariant(emoji.getUnicode());\n        if (variant != null) {\n            appExecutors.getTasksThread().execute(() -> {\n                final Optional<Emoji> first = emoji.getVariants()\n                                                   .stream()\n                                                   .filter(e -> e.getUnicode().equals(variant))\n                                                   .findFirst();\n                if (!first.isPresent()) return;\n                appExecutors.getMainThread().execute(() -> holder.bind(position, first.get(), emoji));\n            });\n            return;\n        }\n        holder.bind(position, emoji, emoji);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return differ.getCurrentList().get(position).hashCode();\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return 0;\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n\n    public static class EmojiViewHolder extends RecyclerView.ViewHolder {\n        // private final AppExecutors appExecutors = AppExecutors.getInstance();\n        private final ItemEmojiGridBinding binding;\n        private final OnEmojiClickListener onEmojiClickListener;\n        private final OnEmojiLongClickListener onEmojiLongClickListener;\n\n        public EmojiViewHolder(@NonNull final ItemEmojiGridBinding binding,\n                               final OnEmojiClickListener onEmojiClickListener,\n                               final OnEmojiLongClickListener onEmojiLongClickListener) {\n            super(binding.getRoot());\n            this.binding = binding;\n            this.onEmojiClickListener = onEmojiClickListener;\n            this.onEmojiLongClickListener = onEmojiLongClickListener;\n        }\n\n        public void bind(final int position, final Emoji emoji, final Emoji parent) {\n            binding.image.setImageDrawable(null);\n            binding.indicator.setVisibility(View.GONE);\n            itemView.setOnLongClickListener(null);\n            // itemView.post(() -> {\n            binding.image.setImageDrawable(emoji.getDrawable());\n            final boolean hasVariants = !parent.getVariants().isEmpty();\n            binding.indicator.setVisibility(hasVariants ? View.VISIBLE : View.GONE);\n            if (onEmojiClickListener != null) {\n                itemView.setOnClickListener(v -> onEmojiClickListener.onClick(v, emoji));\n            }\n            if (hasVariants && onEmojiLongClickListener != null) {\n                itemView.setOnLongClickListener(v -> onEmojiLongClickListener.onLongClick(position, v, parent));\n            }\n            // });\n        }\n    }\n\n    public interface OnEmojiLongClickListener {\n        boolean onLongClick(int position, View view, Emoji emoji);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPicker.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.widget.ImageViewCompat;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport com.google.android.material.tabs.TabLayout;\nimport com.google.android.material.tabs.TabLayoutMediator;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\nimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT;\nimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;\n\npublic class EmojiPicker extends LinearLayout {\n    // private static final String TAG = EmojiPicker.class.getSimpleName();\n\n    public EmojiPicker(final Context context) {\n        super(context);\n        setup();\n    }\n\n    public EmojiPicker(final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        setup();\n    }\n\n    public EmojiPicker(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        setup();\n    }\n\n    private void setup() {\n        setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));\n        setOrientation(VERTICAL);\n    }\n\n    public void init(@NonNull final View rootView,\n                     final OnEmojiClickListener onEmojiClickListener,\n                     final OnBackspaceClickListener onBackspaceClickListener) {\n        final TabLayout tabLayout = new TabLayout(getContext());\n        final LayoutParams tabLayoutLayoutParam = new LayoutParams(MATCH_PARENT, WRAP_CONTENT);\n        tabLayout.setLayoutParams(tabLayoutLayoutParam);\n        tabLayout.setSelectedTabIndicatorGravity(TabLayout.INDICATOR_GRAVITY_TOP);\n        // tabLayout.setSelectedTabIndicatorColor(Utils.getThemeAccentColor(getContext()));\n        tabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.blue_500));\n\n        final ViewPager2 viewPager2 = new ViewPager2(getContext());\n        final LayoutParams viewPagerLayoutParam = new LayoutParams(MATCH_PARENT, 0);\n        viewPagerLayoutParam.weight = 1;\n        viewPager2.setLayoutParams(viewPagerLayoutParam);\n        viewPager2.setAdapter(new EmojiPickerPageAdapter(rootView, onEmojiClickListener));\n        viewPager2.setOffscreenPageLimit(1);\n\n        final Context context = getContext();\n        if (context == null) return;\n        final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context);\n        final List<EmojiCategory> categories = emojiParser.getEmojiCategories();\n\n        new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {\n            tab.view.setPadding(0, 0, 0, 0);\n            final EmojiCategory emojiCategory = categories.get(position);\n            if (emojiCategory == null) return;\n            final Collection<Emoji> emojis = emojiCategory.getEmojis().values();\n            if (emojis.isEmpty()) return;\n            final AppCompatImageView imageView = getImageView();\n            imageView.setImageResource(emojiCategory.getDrawableRes());\n            tab.setCustomView(imageView);\n        }).attach();\n\n        final TabLayout.Tab tab = tabLayout.newTab();\n        tab.view.setPadding(0, 0, 0, 0);\n        final AppCompatImageView imageView = getImageView();\n        imageView.setImageResource(R.drawable.ic_round_backspace_24);\n        final TypedValue outValue = new TypedValue();\n        getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);\n        imageView.setBackgroundResource(outValue.resourceId);\n        imageView.setOnClickListener(v -> {\n            if (onBackspaceClickListener == null) return;\n            onBackspaceClickListener.onClick();\n        });\n        tab.setCustomView(imageView);\n        tab.view.setEnabled(false);\n        tabLayout.addTab(tab);\n        addView(viewPager2);\n        addView(tabLayout);\n    }\n\n    @NonNull\n    private AppCompatImageView getImageView() {\n        final AppCompatImageView imageView = new AppCompatImageView(getContext());\n        imageView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));\n        imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n        ImageViewCompat.setImageTintList(imageView, ContextCompat.getColorStateList(getContext(), R.color.emoji_picker_tab_color));\n        return imageView;\n    }\n\n    public interface OnEmojiClickListener {\n        void onClick(View view, Emoji emoji);\n    }\n\n    public interface OnBackspaceClickListener {\n        void onClick();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPickerPageAdapter.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.AdapterListUpdateCallback;\nimport androidx.recyclerview.widget.AsyncDifferConfig;\nimport androidx.recyclerview.widget.AsyncListDiffer;\nimport androidx.recyclerview.widget.DiffUtil;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener;\nimport awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\nimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT;\n\npublic class EmojiPickerPageAdapter extends RecyclerView.Adapter<EmojiCategoryPageViewHolder> {\n\n    private static final DiffUtil.ItemCallback<EmojiCategory> diffCallback = new DiffUtil.ItemCallback<EmojiCategory>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull final EmojiCategory oldItem, @NonNull final EmojiCategory newItem) {\n            return oldItem.equals(newItem);\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull final EmojiCategory oldItem, @NonNull final EmojiCategory newItem) {\n            return oldItem.equals(newItem);\n        }\n    };\n\n    private final View rootView;\n    private final OnEmojiClickListener onEmojiClickListener;\n    private final AsyncListDiffer<EmojiCategory> differ;\n\n    public EmojiPickerPageAdapter(@NonNull final View rootView,\n                                  final OnEmojiClickListener onEmojiClickListener) {\n        this.rootView = rootView;\n        this.onEmojiClickListener = onEmojiClickListener;\n        differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),\n                                       new AsyncDifferConfig.Builder<>(diffCallback).build());\n        final EmojiParser emojiParser = EmojiParser.Companion.getInstance(rootView.getContext());\n        differ.submitList(emojiParser.getEmojiCategories());\n        setHasStableIds(true);\n    }\n\n    @NonNull\n    @Override\n    public EmojiCategoryPageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {\n        final Context context = parent.getContext();\n        final RecyclerView emojiGrid = new RecyclerView(context);\n        emojiGrid.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));\n        emojiGrid.setLayoutManager(new GridLayoutManager(context, 9));\n        emojiGrid.setHasFixedSize(true);\n        emojiGrid.setClipToPadding(false);\n        emojiGrid.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(8)));\n        return new EmojiCategoryPageViewHolder(rootView, emojiGrid, onEmojiClickListener);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull final EmojiCategoryPageViewHolder holder, final int position) {\n        final EmojiCategory emojiCategory = differ.getCurrentList().get(position);\n        holder.bind(emojiCategory);\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return differ.getCurrentList().get(position).hashCode();\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiVariantManager.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static awais.instagrabber.utils.Constants.PREF_EMOJI_VARIANTS;\n\npublic class EmojiVariantManager {\n    private static final String TAG = EmojiVariantManager.class.getSimpleName();\n    private static final Object LOCK = new Object();\n\n    private final AppExecutors appExecutors = AppExecutors.INSTANCE;\n    private final Map<String, String> selectedVariantMap = new HashMap<>();\n\n    private static EmojiVariantManager instance;\n\n    public static EmojiVariantManager getInstance() {\n        if (instance == null) {\n            synchronized (LOCK) {\n                if (instance == null) {\n                    instance = new EmojiVariantManager();\n                }\n            }\n        }\n        return instance;\n    }\n\n    private EmojiVariantManager() {\n        final String variantsJson = Utils.settingsHelper.getString(PREF_EMOJI_VARIANTS);\n        if (TextUtils.isEmpty(variantsJson)) return;\n        try {\n            final JSONObject variantsJSONObject = new JSONObject(variantsJson);\n            final Iterator<String> keys = variantsJSONObject.keys();\n            keys.forEachRemaining(s -> selectedVariantMap.put(s, variantsJSONObject.optString(s)));\n        } catch (JSONException e) {\n            Log.e(TAG, \"EmojiVariantManager: \", e);\n        }\n    }\n\n    @Nullable\n    public String getVariant(final String parentUnicode) {\n        return selectedVariantMap.get(parentUnicode);\n    }\n\n    public void setVariant(final String parent, final String variant) {\n        if (parent == null || variant == null) return;\n        selectedVariantMap.put(parent, variant);\n        appExecutors.getTasksThread().execute(() -> {\n            final JSONObject jsonObject = new JSONObject(selectedVariantMap);\n            final String json = jsonObject.toString();\n            Utils.settingsHelper.putString(PREF_EMOJI_VARIANTS, json);\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/EmojiVariantPopup.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\n/*\n * Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.PopupWindow;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener;\nimport awais.instagrabber.databinding.ItemEmojiGridBinding;\nimport awais.instagrabber.databinding.LayoutEmojiVariantPopupBinding;\nimport awais.instagrabber.utils.AppExecutors;\n\nimport static android.view.View.MeasureSpec.makeMeasureSpec;\n\npublic final class EmojiVariantPopup {\n    private static final int DO_NOT_UPDATE_FLAG = -1;\n\n    private final View rootView;\n    private final OnEmojiClickListener listener;\n\n    private PopupWindow popupWindow;\n    private View rootImageView;\n    private final EmojiVariantManager emojiVariantManager;\n    private final AppExecutors appExecutors;\n\n    public EmojiVariantPopup(@NonNull final View rootView,\n                             final OnEmojiClickListener listener) {\n        this.rootView = rootView;\n        this.listener = listener;\n        emojiVariantManager = EmojiVariantManager.getInstance();\n        appExecutors = AppExecutors.INSTANCE;\n    }\n\n    public void show(@NonNull final View view, @NonNull final Emoji emoji) {\n        dismiss();\n\n        rootImageView = view;\n\n        final View content = initView(view.getContext(), emoji, view.getWidth());\n\n        popupWindow = new PopupWindow(content, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);\n        popupWindow.setFocusable(true);\n        popupWindow.setOutsideTouchable(true);\n        popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);\n        popupWindow.setBackgroundDrawable(new BitmapDrawable(view.getContext().getResources(), (Bitmap) null));\n\n        content.measure(makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));\n\n        final Point location = locationOnScreen(view);\n        final Point desiredLocation = new Point(\n                location.x - content.getMeasuredWidth() / 2 + view.getWidth() / 2,\n                location.y - content.getMeasuredHeight()\n        );\n\n        popupWindow.showAtLocation(rootView, Gravity.NO_GRAVITY, desiredLocation.x, desiredLocation.y);\n        rootImageView.getParent().requestDisallowInterceptTouchEvent(true);\n        fixPopupLocation(popupWindow, desiredLocation);\n    }\n\n    public void dismiss() {\n        rootImageView = null;\n\n        if (popupWindow != null) {\n            popupWindow.dismiss();\n            popupWindow = null;\n        }\n    }\n\n    private View initView(@NonNull final Context context, @NonNull final Emoji emoji, final int width) {\n        final LayoutInflater layoutInflater = LayoutInflater.from(context);\n        final LayoutEmojiVariantPopupBinding binding = LayoutEmojiVariantPopupBinding.inflate(layoutInflater, null, false);\n        final List<Emoji> variants = new ArrayList<>(emoji.getVariants());\n        // Add parent at start of list\n        // variants.add(0, emoji);\n        for (final Emoji variant : variants) {\n            final ItemEmojiGridBinding itemBinding = ItemEmojiGridBinding.inflate(layoutInflater, binding.container, false);\n            final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) itemBinding.image.getLayoutParams();\n            // Use the same size for Emojis as in the picker.\n            layoutParams.width = width;\n            itemBinding.image.setImageDrawable(variant.getDrawable());\n            itemBinding.image.setOnClickListener(view -> {\n                if (listener != null) {\n                    if (!variant.getUnicode().equals(emojiVariantManager.getVariant(emoji.getUnicode()))) {\n                        emojiVariantManager.setVariant(emoji.getUnicode(), variant.getUnicode());\n                    }\n                    listener.onClick(view, variant);\n                }\n                dismiss();\n            });\n            binding.container.addView(itemBinding.getRoot());\n        }\n        return binding.getRoot();\n    }\n\n    @NonNull\n    private Point locationOnScreen(@NonNull final View view) {\n        final int[] location = new int[2];\n        view.getLocationOnScreen(location);\n        return new Point(location[0], location[1]);\n    }\n\n    private void fixPopupLocation(@NonNull final PopupWindow popupWindow, @NonNull final Point desiredLocation) {\n        popupWindow.getContentView().post(() -> {\n            final Point actualLocation = locationOnScreen(popupWindow.getContentView());\n\n            if (!(actualLocation.x == desiredLocation.x && actualLocation.y == desiredLocation.y)) {\n                final int differenceX = actualLocation.x - desiredLocation.x;\n                final int differenceY = actualLocation.y - desiredLocation.y;\n\n                final int fixedOffsetX;\n                final int fixedOffsetY;\n\n                if (actualLocation.x > desiredLocation.x) {\n                    fixedOffsetX = desiredLocation.x - differenceX;\n                } else {\n                    fixedOffsetX = desiredLocation.x + differenceX;\n                }\n\n                if (actualLocation.y > desiredLocation.y) {\n                    fixedOffsetY = desiredLocation.y - differenceY;\n                } else {\n                    fixedOffsetY = desiredLocation.y + differenceY;\n                }\n\n                popupWindow.update(fixedOffsetX, fixedOffsetY, DO_NOT_UPDATE_FLAG, DO_NOT_UPDATE_FLAG);\n            }\n        });\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/GoogleCompatEmojiDrawable.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\n/*\n * Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario Đanić and contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.text.Spanned;\nimport android.text.TextPaint;\n\nimport androidx.annotation.NonNull;\nimport androidx.emoji.text.EmojiCompat;\nimport androidx.emoji.text.EmojiSpan;\n\n/**\n * An emoji drawable backed by a span generated by the Google emoji support library.\n */\nfinal class GoogleCompatEmojiDrawable extends Drawable {\n    private static final String TAG = GoogleCompatEmojiDrawable.class.getSimpleName();\n    private static final float TEXT_SIZE_FACTOR = 0.8f;\n    private static final float BASELINE_OFFSET_FACTOR = 0.225f;\n\n    private EmojiSpan emojiSpan;\n    private boolean processed;\n    private CharSequence emojiCharSequence;\n    private final TextPaint textPaint = new TextPaint();\n\n    GoogleCompatEmojiDrawable(@NonNull final String unicode) {\n        emojiCharSequence = unicode;\n        textPaint.setStyle(Paint.Style.FILL);\n        textPaint.setColor(0x0ffffffff);\n        textPaint.setAntiAlias(true);\n    }\n\n    private void process() {\n        emojiCharSequence = EmojiCompat.get().process(emojiCharSequence);\n        if (emojiCharSequence instanceof Spanned) {\n            final Object[] spans = ((Spanned) emojiCharSequence).getSpans(0, emojiCharSequence.length(), EmojiSpan.class);\n            if (spans.length > 0) {\n                emojiSpan = (EmojiSpan) spans[0];\n            }\n        }\n    }\n\n    @Override\n    public void draw(@NonNull final Canvas canvas) {\n        final Rect bounds = getBounds();\n        textPaint.setTextSize(bounds.height() * TEXT_SIZE_FACTOR);\n        final int y = Math.round(bounds.bottom - bounds.height() * BASELINE_OFFSET_FACTOR);\n\n        if (!processed && EmojiCompat.get().getLoadState() != EmojiCompat.LOAD_STATE_LOADING) {\n            processed = true;\n            if (EmojiCompat.get().getLoadState() != EmojiCompat.LOAD_STATE_FAILED) {\n                process();\n            }\n        }\n\n        if (emojiSpan == null) {\n            canvas.drawText(emojiCharSequence, 0, emojiCharSequence.length(), bounds.left, y, textPaint);\n        } else {\n            emojiSpan.draw(canvas, emojiCharSequence, 0, emojiCharSequence.length(), bounds.left, bounds.top, y, bounds.bottom, textPaint);\n        }\n    }\n\n    @Override\n    public void setAlpha(final int alpha) {\n        textPaint.setAlpha(alpha);\n    }\n\n    @Override\n    public void setColorFilter(final ColorFilter colorFilter) {\n        textPaint.setColorFilter(colorFilter);\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.UNKNOWN;\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/emoji/ReactionsManager.java",
    "content": "package awais.instagrabber.customviews.emoji;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableList;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.utils.emoji.EmojiParser;\n\nimport static awais.instagrabber.utils.Constants.PREF_REACTIONS;\n\npublic class ReactionsManager {\n    private static final String TAG = ReactionsManager.class.getSimpleName();\n    private static final Object LOCK = new Object();\n\n    // private final AppExecutors appExecutors = AppExecutors.INSTANCE;\n    private final List<Emoji> reactions = new ArrayList<>();\n\n    private static ReactionsManager instance;\n\n    public static ReactionsManager getInstance(@NonNull final Context context) {\n        if (instance == null) {\n            synchronized (LOCK) {\n                if (instance == null) {\n                    instance = new ReactionsManager(context);\n                }\n            }\n        }\n        return instance;\n    }\n\n    private ReactionsManager(@NonNull final Context context) {\n        final EmojiParser emojiParser = EmojiParser.Companion.getInstance(context);\n        String reactionsJson = Utils.settingsHelper.getString(PREF_REACTIONS);\n        if (TextUtils.isEmpty(reactionsJson)) {\n            final ImmutableList<String> list = ImmutableList.of(\"❤️\", \"\\uD83D\\uDE02\", \"\\uD83D\\uDE2E\", \"\\uD83D\\uDE22\", \"\\uD83D\\uDE21\", \"\\uD83D\\uDC4D\");\n            reactionsJson = new JSONArray(list).toString();\n        }\n        final Map<String, Emoji> allEmojis = emojiParser.getAllEmojis();\n        try {\n            final JSONArray reactionsJsonArray = new JSONArray(reactionsJson);\n            for (int i = 0; i < reactionsJsonArray.length(); i++) {\n                final String emojiUnicode = reactionsJsonArray.optString(i);\n                if (emojiUnicode == null) continue;\n                final Emoji emoji = allEmojis.get(emojiUnicode);\n                if (emoji == null) continue;\n                reactions.add(emoji);\n            }\n        } catch (JSONException e) {\n            Log.e(TAG, \"ReactionsManager: \", e);\n        }\n    }\n\n    public List<Emoji> getReactions() {\n        return reactions;\n    }\n\n    // public void setVariant(final String parent, final String variant) {\n    //     if (parent == null || variant == null) return;\n    //     selectedVariantMap.put(parent, variant);\n    //     appExecutors.tasksThread().execute(() -> {\n    //         final JSONObject jsonObject = new JSONObject(selectedVariantMap);\n    //         final String json = jsonObject.toString();\n    //         Utils.settingsHelper.putString(PREF_EMOJI_VARIANTS, json);\n    //     });\n    // }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/ChangeText.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\n/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ValueAnimator;\nimport android.graphics.Color;\nimport android.util.Log;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.transition.Transition;\nimport androidx.transition.TransitionListenerAdapter;\nimport androidx.transition.TransitionValues;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport awais.instagrabber.BuildConfig;\n\n/**\n * This transition tracks changes to the text in TextView targets. If the text\n * changes between the start and end scenes, the transition ensures that the\n * starting text stays until the transition ends, at which point it changes\n * to the end text.  This is useful in situations where you want to resize a\n * text view to its new size before displaying the text that goes there.\n */\npublic class ChangeText extends Transition {\n    private static final String LOG_TAG = \"TextChange\";\n    private static final String PROPNAME_TEXT = \"android:textchange:text\";\n    private static final String PROPNAME_TEXT_SELECTION_START =\n            \"android:textchange:textSelectionStart\";\n    private static final String PROPNAME_TEXT_SELECTION_END =\n            \"android:textchange:textSelectionEnd\";\n    private static final String PROPNAME_TEXT_COLOR = \"android:textchange:textColor\";\n    private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP;\n    private boolean crossFade;\n    /**\n     * Flag specifying that the text in affected/changing TextView targets will keep\n     * their original text during the transition, setting it to the final text when\n     * the transition ends. This is the default behavior.\n     *\n     * @see #setChangeBehavior(int)\n     */\n    public static final int CHANGE_BEHAVIOR_KEEP = 0;\n    /**\n     * Flag specifying that the text changing animation should first fade\n     * out the original text completely. The new text is set on the target\n     * view at the end of the fade-out animation. This transition is typically\n     * used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more\n     * flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other\n     * transitions to be run sequentially or in parallel with these fades.\n     *\n     * @see #setChangeBehavior(int)\n     */\n    public static final int CHANGE_BEHAVIOR_OUT = 1;\n    /**\n     * Flag specifying that the text changing animation should fade in the\n     * end text into the affected target view(s). This transition is typically\n     * used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT}\n     * transition, possibly with other transitions running as well, such as\n     * a sequence to fade out, then resize the view, then fade in.\n     *\n     * @see #setChangeBehavior(int)\n     */\n    public static final int CHANGE_BEHAVIOR_IN = 2;\n    /**\n     * Flag specifying that the text changing animation should first fade\n     * out the original text completely and then fade in the\n     * new text.\n     *\n     * @see #setChangeBehavior(int)\n     */\n    public static final int CHANGE_BEHAVIOR_OUT_IN = 3;\n    private static final String[] sTransitionProperties = {\n            PROPNAME_TEXT,\n            PROPNAME_TEXT_SELECTION_START,\n            PROPNAME_TEXT_SELECTION_END\n    };\n\n    /**\n     * Sets the type of changing animation that will be run, one of\n     * {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT},\n     * {@link #CHANGE_BEHAVIOR_IN}, and {@link #CHANGE_BEHAVIOR_OUT_IN}.\n     *\n     * @param changeBehavior The type of fading animation to use when this\n     *                       transition is run.\n     * @return this textChange object.\n     */\n    public ChangeText setChangeBehavior(int changeBehavior) {\n        if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) {\n            mChangeBehavior = changeBehavior;\n        }\n        return this;\n    }\n\n    public ChangeText setCrossFade(final boolean crossFade) {\n        this.crossFade = crossFade;\n        return this;\n    }\n\n    @Override\n    public String[] getTransitionProperties() {\n        return sTransitionProperties;\n    }\n\n    /**\n     * Returns the type of changing animation that will be run.\n     *\n     * @return either {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT},\n     * {@link #CHANGE_BEHAVIOR_IN}, or {@link #CHANGE_BEHAVIOR_OUT_IN}.\n     */\n    public int getChangeBehavior() {\n        return mChangeBehavior;\n    }\n\n    private void captureValues(TransitionValues transitionValues) {\n        if (transitionValues.view instanceof TextView) {\n            TextView textview = (TextView) transitionValues.view;\n            transitionValues.values.put(PROPNAME_TEXT, textview.getText());\n            if (textview instanceof EditText) {\n                transitionValues.values.put(PROPNAME_TEXT_SELECTION_START,\n                                            textview.getSelectionStart());\n                transitionValues.values.put(PROPNAME_TEXT_SELECTION_END,\n                                            textview.getSelectionEnd());\n            }\n            if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {\n                transitionValues.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor());\n            }\n        }\n    }\n\n    @Override\n    public void captureStartValues(@NonNull TransitionValues transitionValues) {\n        captureValues(transitionValues);\n    }\n\n    @Override\n    public void captureEndValues(@NonNull TransitionValues transitionValues) {\n        captureValues(transitionValues);\n    }\n\n    @Override\n    public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,\n                                   TransitionValues endValues) {\n        if (startValues == null || endValues == null ||\n                !(startValues.view instanceof TextView) || !(endValues.view instanceof TextView)) {\n            return null;\n        }\n        final TextView view = (TextView) endValues.view;\n        Map<String, Object> startVals = startValues.values;\n        Map<String, Object> endVals = endValues.values;\n        final CharSequence startText = startVals.get(PROPNAME_TEXT) != null ?\n                                       (CharSequence) startVals.get(PROPNAME_TEXT) : \"\";\n        final CharSequence endText = endVals.get(PROPNAME_TEXT) != null ?\n                                     (CharSequence) endVals.get(PROPNAME_TEXT) : \"\";\n        final int startSelectionStart, startSelectionEnd, endSelectionStart, endSelectionEnd;\n        if (view instanceof EditText) {\n            startSelectionStart = startVals.get(PROPNAME_TEXT_SELECTION_START) != null ?\n                                  (Integer) startVals.get(PROPNAME_TEXT_SELECTION_START) : -1;\n            startSelectionEnd = startVals.get(PROPNAME_TEXT_SELECTION_END) != null ?\n                                (Integer) startVals.get(PROPNAME_TEXT_SELECTION_END) : startSelectionStart;\n            endSelectionStart = endVals.get(PROPNAME_TEXT_SELECTION_START) != null ?\n                                (Integer) endVals.get(PROPNAME_TEXT_SELECTION_START) : -1;\n            endSelectionEnd = endVals.get(PROPNAME_TEXT_SELECTION_END) != null ?\n                              (Integer) endVals.get(PROPNAME_TEXT_SELECTION_END) : endSelectionStart;\n        } else {\n            startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1;\n        }\n        if (!Objects.equals(startText, endText)) {\n            final int startColor;\n            final int endColor;\n            if (mChangeBehavior != CHANGE_BEHAVIOR_IN) {\n                view.setText(startText);\n                if (view instanceof EditText) {\n                    setSelection(((EditText) view), startSelectionStart, startSelectionEnd);\n                }\n            }\n            Animator anim;\n            if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) {\n                startColor = endColor = 0;\n                anim = ValueAnimator.ofFloat(0, 1);\n                anim.addListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        if (Objects.equals(startText, view.getText())) {\n                            // Only set if it hasn't been changed since anim started\n                            view.setText(endText);\n                            if (view instanceof EditText) {\n                                setSelection(((EditText) view), endSelectionStart, endSelectionEnd);\n                            }\n                        }\n                    }\n                });\n            } else {\n                startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR);\n                endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR);\n                // Fade out start text\n                ValueAnimator outAnim = null, inAnim = null;\n                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||\n                        mChangeBehavior == CHANGE_BEHAVIOR_OUT) {\n                    outAnim = ValueAnimator.ofInt(Color.alpha(startColor), 0);\n                    outAnim.addUpdateListener(animation -> {\n                        int currAlpha = (Integer) animation.getAnimatedValue();\n                        view.setTextColor(currAlpha << 24 | startColor & 0xffffff);\n                    });\n                    outAnim.addListener(new AnimatorListenerAdapter() {\n                        @Override\n                        public void onAnimationEnd(Animator animation) {\n                            if (Objects.equals(startText, view.getText())) {\n                                // Only set if it hasn't been changed since anim started\n                                view.setText(endText);\n                                if (view instanceof EditText) {\n                                    setSelection(((EditText) view), endSelectionStart,\n                                                 endSelectionEnd);\n                                }\n                            }\n                            // restore opaque alpha and correct end color\n                            view.setTextColor(endColor);\n                        }\n                    });\n                }\n                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||\n                        mChangeBehavior == CHANGE_BEHAVIOR_IN) {\n                    inAnim = ValueAnimator.ofInt(0, Color.alpha(endColor));\n                    inAnim.addUpdateListener(animation -> {\n                        int currAlpha = (Integer) animation.getAnimatedValue();\n                        view.setTextColor(currAlpha << 24 | endColor & 0xffffff);\n                    });\n                    inAnim.addListener(new AnimatorListenerAdapter() {\n                        @Override\n                        public void onAnimationCancel(Animator animation) {\n                            // restore opaque alpha and correct end color\n                            view.setTextColor(endColor);\n                        }\n                    });\n                }\n                if (outAnim != null && inAnim != null) {\n                    anim = new AnimatorSet();\n                    final AnimatorSet animatorSet = (AnimatorSet) anim;\n                    if (crossFade) {\n                        animatorSet.playTogether(outAnim, inAnim);\n                    } else {\n                        animatorSet.playSequentially(outAnim, inAnim);\n                    }\n                } else if (outAnim != null) {\n                    anim = outAnim;\n                } else {\n                    // Must be an in-only animation\n                    anim = inAnim;\n                }\n            }\n            TransitionListener transitionListener = new TransitionListenerAdapter() {\n                int mPausedColor = 0;\n\n                @Override\n                public void onTransitionPause(@NonNull Transition transition) {\n                    if (mChangeBehavior != CHANGE_BEHAVIOR_IN) {\n                        view.setText(endText);\n                        if (view instanceof EditText) {\n                            setSelection(((EditText) view), endSelectionStart, endSelectionEnd);\n                        }\n                    }\n                    if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {\n                        mPausedColor = view.getCurrentTextColor();\n                        view.setTextColor(endColor);\n                    }\n                }\n\n                @Override\n                public void onTransitionResume(@NonNull Transition transition) {\n                    if (mChangeBehavior != CHANGE_BEHAVIOR_IN) {\n                        view.setText(startText);\n                        if (view instanceof EditText) {\n                            setSelection(((EditText) view), startSelectionStart, startSelectionEnd);\n                        }\n                    }\n                    if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {\n                        view.setTextColor(mPausedColor);\n                    }\n                }\n\n                @Override\n                public void onTransitionEnd(Transition transition) {\n                    transition.removeListener(this);\n                }\n            };\n            addListener(transitionListener);\n            if (BuildConfig.DEBUG) {\n                Log.d(LOG_TAG, \"createAnimator returning \" + anim);\n            }\n            return anim;\n        }\n        return null;\n    }\n\n    private void setSelection(EditText editText, int start, int end) {\n        if (start >= 0 && end >= 0) {\n            editText.setSelection(start, end);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/ControlFocusInsetsAnimationCallback.java",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage awais.instagrabber.customviews.helpers;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.List;\n\n/**\n * A [WindowInsetsAnimationCompat.Callback] which will request and clear focus on the given view,\n * depending on the [WindowInsetsCompat.Type.ime] visibility state when an IME\n * [WindowInsetsAnimationCompat] has finished.\n * <p>\n * This is primarily used when animating the [WindowInsetsCompat.Type.ime], so that the\n * appropriate view is focused for accepting input from the IME.\n */\npublic class ControlFocusInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback {\n\n    private final View view;\n\n    public ControlFocusInsetsAnimationCallback(@NonNull final View view) {\n        this(view, DISPATCH_MODE_STOP);\n    }\n\n    /**\n     * @param view         the view to request/clear focus\n     * @param dispatchMode The dispatch mode for this callback.\n     * @see WindowInsetsAnimationCompat.Callback.DispatchMode\n     */\n    public ControlFocusInsetsAnimationCallback(@NonNull final View view, final int dispatchMode) {\n        super(dispatchMode);\n        this.view = view;\n    }\n\n    @NonNull\n    @Override\n    public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets,\n                                         @NonNull final List<WindowInsetsAnimationCompat> runningAnimations) {\n        // no-op and return the insets\n        return insets;\n    }\n\n    @Override\n    public void onEnd(final WindowInsetsAnimationCompat animation) {\n        if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {\n            // The animation has now finished, so we can check the view's focus state.\n            // We post the check because the rootWindowInsets has not yet been updated, but will\n            // be in the next message traversal\n            view.post(this::checkFocus);\n        }\n    }\n\n    private void checkFocus() {\n        final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);\n        boolean imeVisible = false;\n        if (rootWindowInsets != null) {\n            imeVisible = rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime());\n        }\n        if (imeVisible && view.getRootView().findFocus() == null) {\n            // If the IME will be visible, and there is not a currently focused view in\n            // the hierarchy, request focus on our view\n            view.requestFocus();\n        } else if (!imeVisible && view.isFocused()) {\n            // If the IME will not be visible and our view is currently focused, clear the focus\n            view.clearFocus();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/CustomHideBottomViewOnScrollBehavior.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.ViewCompat;\n\nimport com.google.android.material.behavior.HideBottomViewOnScrollBehavior;\nimport com.google.android.material.bottomnavigation.BottomNavigationView;\n\npublic class CustomHideBottomViewOnScrollBehavior extends HideBottomViewOnScrollBehavior<BottomNavigationView> {\n    private static final String TAG = \"CustomHideBottomView\";\n\n    public CustomHideBottomViewOnScrollBehavior() {\n    }\n\n    public CustomHideBottomViewOnScrollBehavior(final Context context, final AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onStartNestedScroll(@NonNull final CoordinatorLayout coordinatorLayout,\n                                       @NonNull final BottomNavigationView child,\n                                       @NonNull final View directTargetChild,\n                                       @NonNull final View target,\n                                       final int nestedScrollAxes,\n                                       final int type) {\n        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;\n    }\n\n    @Override\n    public void onNestedPreScroll(@NonNull final CoordinatorLayout coordinatorLayout,\n                                  @NonNull final BottomNavigationView child,\n                                  @NonNull final View target,\n                                  final int dx,\n                                  final int dy,\n                                  @NonNull final int[] consumed,\n                                  final int type) {\n        if (dy > 0) {\n            slideDown(child);\n        } else if (dy < 0) {\n            slideUp(child);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/EmojiPickerInsetsAnimationCallback.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.List;\n\n/**\n * A customized {@link TranslateDeferringInsetsAnimationCallback} for the emoji picker\n */\npublic class EmojiPickerInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback {\n    private static final String TAG = EmojiPickerInsetsAnimationCallback.class.getSimpleName();\n\n    private final View view;\n    private final int persistentInsetTypes;\n    private final int deferredInsetTypes;\n\n    private int kbHeight;\n    private onKbVisibilityChangeListener listener;\n    private boolean shouldTranslate;\n\n    public EmojiPickerInsetsAnimationCallback(final View view,\n                                              final int persistentInsetTypes,\n                                              final int deferredInsetTypes) {\n        this(view, persistentInsetTypes, deferredInsetTypes, DISPATCH_MODE_STOP);\n    }\n\n    public EmojiPickerInsetsAnimationCallback(final View view,\n                                              final int persistentInsetTypes,\n                                              final int deferredInsetTypes,\n                                              final int dispatchMode) {\n        super(dispatchMode);\n        if ((persistentInsetTypes & deferredInsetTypes) != 0) {\n            throw new IllegalArgumentException(\"persistentInsetTypes and deferredInsetTypes can not contain \" +\n                                                       \"any of same WindowInsetsCompat.Type values\");\n        }\n        this.view = view;\n        this.persistentInsetTypes = persistentInsetTypes;\n        this.deferredInsetTypes = deferredInsetTypes;\n    }\n\n    @NonNull\n    @Override\n    public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets,\n                                         @NonNull final List<WindowInsetsAnimationCompat> runningAnimations) {\n        // onProgress() is called when any of the running animations progress...\n\n        // First we get the insets which are potentially deferred\n        final Insets typesInset = insets.getInsets(deferredInsetTypes);\n        // Then we get the persistent inset types which are applied as padding during layout\n        final Insets otherInset = insets.getInsets(persistentInsetTypes);\n\n        // Now that we subtract the two insets, to calculate the difference. We also coerce\n        // the insets to be >= 0, to make sure we don't use negative insets.\n        final Insets subtract = Insets.subtract(typesInset, otherInset);\n        final Insets diff = Insets.max(subtract, Insets.NONE);\n\n        // The resulting `diff` insets contain the values for us to apply as a translation\n        // to the view\n        view.setTranslationX(diff.left - diff.right);\n        view.setTranslationY(shouldTranslate ? diff.top - diff.bottom : -kbHeight);\n\n        return insets;\n    }\n\n    @Override\n    public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) {\n        try {\n            final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);\n            if (kbHeight == 0) {\n                if (rootWindowInsets == null) return;\n                final Insets imeInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.ime());\n                final Insets navBarInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());\n                kbHeight = imeInsets.bottom - navBarInsets.bottom;\n                final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();\n                if (layoutParams != null) {\n                    layoutParams.height = kbHeight;\n                    layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, -kbHeight);\n                }\n            }\n            view.setTranslationX(0f);\n            final boolean visible = rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime());\n            float translationY = 0;\n            if (!shouldTranslate) {\n                translationY = -kbHeight;\n                if (visible) {\n                    translationY = 0;\n                }\n            }\n            view.setTranslationY(translationY);\n\n            if (listener != null && rootWindowInsets != null) {\n                listener.onChange(visible);\n            }\n        } finally {\n            shouldTranslate = true;\n        }\n    }\n\n    public void setShouldTranslate(final boolean shouldTranslate) {\n        this.shouldTranslate = shouldTranslate;\n    }\n\n    public void setKbVisibilityListener(final onKbVisibilityChangeListener listener) {\n        this.listener = listener;\n    }\n\n    public interface onKbVisibilityChangeListener {\n        void onChange(boolean isVisible);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/GridAutofitLayoutManager.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.content.Context;\n\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.utils.Utils;\n\npublic class GridAutofitLayoutManager extends GridLayoutManager {\n    private int mColumnWidth;\n    private boolean mColumnWidthChanged = true;\n\n    public GridAutofitLayoutManager(Context context, int columnWidth) {\n        super(context, 1);\n        if (columnWidth <= 0) columnWidth = (int) (48 * Utils.displayMetrics.density);\n        if (columnWidth > 0 && columnWidth != mColumnWidth) {\n            mColumnWidth = columnWidth;\n            mColumnWidthChanged = true;\n        }\n    }\n\n    @Override\n    public void onLayoutChildren(final RecyclerView.Recycler recycler, final RecyclerView.State state) {\n        final int width = getWidth();\n        final int height = getHeight();\n        if (mColumnWidthChanged && mColumnWidth > 0 && width > 0 && height > 0) {\n            final int totalSpace = getOrientation() == VERTICAL ? width - getPaddingRight() - getPaddingLeft()\n                    : height - getPaddingTop() - getPaddingBottom();\n\n            setSpanCount(Math.max(1, Math.min(totalSpace / mColumnWidth, 3)));\n\n            mColumnWidthChanged = false;\n        }\n        super.onLayoutChildren(recycler, state);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.graphics.Rect;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {\n    private final int halfSpace;\n\n    private boolean hasHeader;\n\n    public GridSpacingItemDecoration(int spacing) {\n        halfSpace = spacing / 2;\n    }\n\n    @Override\n    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        if (hasHeader && parent.getChildAdapterPosition(view) == 0) {\n            outRect.bottom = halfSpace;\n            outRect.left = -halfSpace;\n            outRect.right = -halfSpace;\n            return;\n        }\n        if (parent.getPaddingLeft() != halfSpace) {\n            parent.setPadding(halfSpace, hasHeader ? 0 : halfSpace, halfSpace, halfSpace);\n            parent.setClipToPadding(false);\n        }\n        outRect.top = halfSpace;\n        outRect.bottom = halfSpace;\n        outRect.left = halfSpace;\n        outRect.right = halfSpace;\n    }\n\n    public void setHasHeader(final boolean hasHeader) {\n        this.hasHeader = hasHeader;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/HeaderItemDecoration.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.util.Pair;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * Java implementation of <a href=\"https://gist.github.com/filipkowicz/1a769001fae407b8813ab4387c42fcbd/3cda7542b12100b01da449e8648368b8f1369c70\">this gist</a> by filipkowicz\n */\npublic class HeaderItemDecoration extends RecyclerView.ItemDecoration {\n    private static final String TAG = HeaderItemDecoration.class.getSimpleName();\n\n    private final HeaderItemDecorationCallback callback;\n\n    private boolean layoutReversed = false;\n    private Pair<Integer, RecyclerView.ViewHolder> currentHeader;\n\n    public HeaderItemDecoration(@NonNull RecyclerView parent,\n                                @NonNull HeaderItemDecorationCallback callback) {\n        this.callback = callback;\n        final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();\n        if (layoutManager instanceof LinearLayoutManager) {\n            layoutReversed = ((LinearLayoutManager) layoutManager).getReverseLayout();\n        }\n        //noinspection rawtypes\n        final RecyclerView.Adapter adapter = parent.getAdapter();\n        if (adapter == null) return;\n        adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            @Override\n            public void onChanged() {\n                // clear saved header as it can be outdated now\n                Log.d(TAG, \"registerAdapterDataObserver\");\n                currentHeader = null;\n            }\n        });\n        parent.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {\n            // clear saved layout as it may need layout update\n            Log.d(TAG, \"addOnLayoutChangeListener\");\n            currentHeader = null;\n        });\n        parent.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {\n            @Override\n            public boolean onInterceptTouchEvent(@NonNull final RecyclerView rv, @NonNull final MotionEvent e) {\n                if (e.getAction() == MotionEvent.ACTION_DOWN && currentHeader != null) {\n                    final RecyclerView.ViewHolder viewHolder = currentHeader.second;\n                    if (viewHolder != null && viewHolder.itemView != null) {\n                        final int bottom = viewHolder.itemView.getBottom();\n                        return e.getY() <= bottom;\n                    }\n                }\n                return super.onInterceptTouchEvent(rv, e);\n            }\n        });\n    }\n\n    @Override\n    public void onDrawOver(@NonNull final Canvas c, @NonNull final RecyclerView parent, @NonNull final RecyclerView.State state) {\n        super.onDrawOver(c, parent, state);\n        final View topChild = parent.findChildViewUnder(\n                parent.getPaddingLeft(),\n                parent.getPaddingTop()\n        );\n        if (topChild == null) {\n            return;\n        }\n        final int topChildPosition = parent.getChildAdapterPosition(topChild);\n        if (topChildPosition == RecyclerView.NO_POSITION) {\n            return;\n        }\n        final View headerView = getHeaderViewForItem(topChildPosition, parent);\n        if (headerView == null) {\n            return;\n        }\n        final int contactPoint = headerView.getBottom() + parent.getPaddingTop();\n        final View childInContact = getChildInContact(parent, contactPoint);\n        if (childInContact != null && callback.isHeader(parent.getChildAdapterPosition(childInContact))) {\n            moveHeader(c, headerView, childInContact, parent.getPaddingTop());\n            return;\n        }\n        drawHeader(c, headerView, parent.getPaddingTop());\n    }\n\n    private void drawHeader(@NonNull final Canvas c, @NonNull final View header, final int paddingTop) {\n        c.save();\n        c.translate(0f, paddingTop);\n        header.draw(c);\n        c.restore();\n    }\n\n    private void moveHeader(@NonNull final Canvas c, @NonNull final View currentHeader, @NonNull final View nextHeader, final int paddingTop) {\n        c.save();\n        c.translate(0f, nextHeader.getTop() - currentHeader.getHeight() /*+ paddingTop*/);\n        currentHeader.draw(c);\n        c.restore();\n    }\n\n    @Nullable\n    private View getChildInContact(@NonNull final RecyclerView parent, final int contactPoint) {\n        View childInContact = null;\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final Rect mBounds = new Rect();\n            parent.getDecoratedBoundsWithMargins(child, mBounds);\n            if (mBounds.bottom > contactPoint) {\n                if (mBounds.top <= contactPoint) {\n                    // This child overlaps the contactPoint\n                    childInContact = child;\n                    break;\n                }\n            }\n        }\n        return childInContact;\n    }\n\n    @Nullable\n    private View getHeaderViewForItem(final int itemPosition, @NonNull final RecyclerView parent) {\n        if (parent.getAdapter() == null) {\n            return null;\n        }\n        final int headerPosition = getHeaderPositionForItem(itemPosition, parent.getAdapter());\n        if (headerPosition == RecyclerView.NO_POSITION) return null;\n        final int headerType = parent.getAdapter().getItemViewType(headerPosition);\n        // if match reuse viewHolder\n        if (currentHeader != null\n                && currentHeader.first == headerPosition\n                && currentHeader.second.getItemViewType() == headerType) {\n            return currentHeader.second.itemView;\n        }\n        final RecyclerView.ViewHolder headerHolder = parent.getAdapter().createViewHolder(parent, headerType);\n        if (headerHolder != null) {\n            //noinspection unchecked\n            parent.getAdapter().onBindViewHolder(headerHolder, headerPosition);\n            fixLayoutSize(parent, headerHolder.itemView);\n            // save for next draw\n            currentHeader = new Pair<>(headerPosition, headerHolder);\n            return headerHolder.itemView;\n        }\n        return null;\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    private int getHeaderPositionForItem(final int itemPosition, final RecyclerView.Adapter adapter) {\n        int headerPosition = RecyclerView.NO_POSITION;\n        int currentPosition = itemPosition;\n        do {\n            if (callback.isHeader(currentPosition)) {\n                headerPosition = currentPosition;\n                break;\n            }\n            currentPosition += layoutReversed ? 1 : -1;\n        } while (layoutReversed ? currentPosition < adapter.getItemCount() : currentPosition >= 0);\n        return headerPosition;\n    }\n\n    /**\n     * Properly measures and layouts the top sticky header.\n     *\n     * @param parent ViewGroup: RecyclerView in this case.\n     */\n    private void fixLayoutSize(@NonNull final ViewGroup parent, @NonNull final View view) {\n\n        // Specs for parent (RecyclerView)\n        final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);\n        final int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);\n\n        // Specs for children (headers)\n        final int childWidthSpec = ViewGroup.getChildMeasureSpec(\n                widthSpec,\n                parent.getPaddingLeft() + parent.getPaddingRight(),\n                view.getLayoutParams().width\n        );\n        final int childHeightSpec = ViewGroup.getChildMeasureSpec(\n                heightSpec,\n                parent.getPaddingTop() + parent.getPaddingBottom(),\n                view.getLayoutParams().height\n        );\n\n        view.measure(childWidthSpec, childHeightSpec);\n        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());\n    }\n\n    public View getCurrentHeader() {\n        return currentHeader == null ? null : currentHeader.second.itemView;\n    }\n\n    public interface HeaderItemDecorationCallback {\n        boolean isHeader(int itemPosition);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/HeightProvider.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.app.Activity;\nimport android.graphics.Rect;\nimport android.graphics.drawable.ColorDrawable;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewTreeObserver.OnGlobalLayoutListener;\nimport android.view.WindowManager.LayoutParams;\nimport android.widget.PopupWindow;\n\npublic class HeightProvider extends PopupWindow implements OnGlobalLayoutListener {\n    private final Activity mActivity;\n    private final View rootView;\n    private HeightListener listener;\n    private int heightMax;\n\n    public HeightProvider(Activity activity) {\n        super(activity);\n        this.mActivity = activity;\n\n        rootView = new View(activity);\n        setContentView(rootView);\n\n        rootView.getViewTreeObserver().addOnGlobalLayoutListener(this);\n        setBackgroundDrawable(new ColorDrawable(0));\n\n        setWidth(0);\n        setHeight(LayoutParams.MATCH_PARENT);\n\n        setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE);\n        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);\n    }\n\n    public HeightProvider init() {\n        if (!isShowing()) {\n            final View view = mActivity.getWindow().getDecorView();\n            view.post(() -> showAtLocation(view, Gravity.NO_GRAVITY, 0, 0));\n        }\n        return this;\n    }\n\n    public HeightProvider setHeightListener(HeightListener listener) {\n        this.listener = listener;\n        return this;\n    }\n\n    @Override\n    public void onGlobalLayout() {\n        Rect rect = new Rect();\n        rootView.getWindowVisibleDisplayFrame(rect);\n        if (rect.bottom > heightMax) {\n            heightMax = rect.bottom;\n        }\n        \n        int keyboardHeight = heightMax - rect.bottom;\n        if (listener != null) {\n            listener.onHeightChanged(keyboardHeight);\n        }\n    }\n\n    public interface HeightListener {\n        void onHeightChanged(int height);\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/ImageResizingControllerListener.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.graphics.drawable.Animatable;\nimport android.view.ViewGroup;\n\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.drawee.generic.GenericDraweeHierarchy;\nimport com.facebook.drawee.view.DraweeView;\nimport com.facebook.imagepipeline.image.ImageInfo;\n\nimport awais.instagrabber.utils.NumberUtils;\n\npublic class ImageResizingControllerListener<T extends DraweeView<GenericDraweeHierarchy>> extends BaseControllerListener<ImageInfo> {\n    private static final String TAG = \"ImageResizingController\";\n\n    private T imageView;\n    private final int requiredWidth;\n\n    public ImageResizingControllerListener(final T imageView, final int requiredWidth) {\n        this.imageView = imageView;\n        this.requiredWidth = requiredWidth;\n    }\n\n    @Override\n    public void onIntermediateImageSet(final String id, final ImageInfo imageInfo) {\n        super.onIntermediateImageSet(id, imageInfo);\n    }\n\n    public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {\n        if (imageInfo != null) {\n            // updateViewSize(imageInfo);\n            final int height = imageInfo.getHeight();\n            final int width = imageInfo.getWidth();\n            // final float aspectRatio = ((float) width) / height;\n            final ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();\n            // final int deviceWidth = Utils.displayMetrics.widthPixels;\n            final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, height, width);\n            layoutParams.width = requiredWidth;\n            layoutParams.height = resultingHeight;\n            imageView.requestLayout();\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/NestedCoordinatorLayout.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.NestedScrollingChild;\nimport androidx.core.view.NestedScrollingChildHelper;\n\npublic class NestedCoordinatorLayout extends CoordinatorLayout implements NestedScrollingChild {\n\n    private NestedScrollingChildHelper mChildHelper;\n\n    public NestedCoordinatorLayout(Context context) {\n        super(context);\n        mChildHelper = new NestedScrollingChildHelper(this);\n        setNestedScrollingEnabled(true);\n    }\n\n    public NestedCoordinatorLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        mChildHelper = new NestedScrollingChildHelper(this);\n        setNestedScrollingEnabled(true);\n    }\n\n    public NestedCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        mChildHelper = new NestedScrollingChildHelper(this);\n        setNestedScrollingEnabled(true);\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) {\n        int[][] tConsumed = new int[2][2];\n        super.onNestedPreScroll(target, dx, dy, consumed, type);\n        dispatchNestedPreScroll(dx, dy, tConsumed[1], null);\n        consumed[0] = tConsumed[0][0] + tConsumed[1][0];\n        consumed[1] = tConsumed[0][1] + tConsumed[1][1];\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {\n        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);\n        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);\n    }\n\n    @Override\n    public void onStopNestedScroll(View target, int type) {\n        /* Disable the scrolling behavior of our own children */\n        super.onStopNestedScroll(target, type);\n        /* Disable the scrolling behavior of the parent's other children  */\n        stopNestedScroll();\n    }\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes, int type) {\n        /* Enable the scrolling behavior of our own children */\n        boolean tHandled = super.onStartNestedScroll(child, target, nestedScrollAxes, type);\n        /* Enable the scrolling behavior of the parent's other children  */\n        return startNestedScroll(nestedScrollAxes) || tHandled;\n    }\n\n    @Override\n    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {\n        /* Enable the scrolling behavior of our own children */\n        boolean tHandled = super.onStartNestedScroll(child, target, nestedScrollAxes);\n        /* Enable the scrolling behavior of the parent's other children  */\n        return startNestedScroll(nestedScrollAxes) || tHandled;\n    }\n\n    @Override\n    public void onStopNestedScroll(View target) {\n        /* Disable the scrolling behavior of our own children */\n        super.onStopNestedScroll(target);\n        /* Disable the scrolling behavior of the parent's other children  */\n        stopNestedScroll();\n    }\n\n    @Override\n    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {\n        int[][] tConsumed = new int[2][2];\n        super.onNestedPreScroll(target, dx, dy, tConsumed[0]);\n        dispatchNestedPreScroll(dx, dy, tConsumed[1], null);\n        consumed[0] = tConsumed[0][0] + tConsumed[1][0];\n        consumed[1] = tConsumed[0][1] + tConsumed[1][1];\n    }\n\n    @Override\n    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,\n                               int dxUnconsumed, int dyUnconsumed) {\n        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);\n        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);\n    }\n\n    @Override\n    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {\n        boolean tHandled = super.onNestedPreFling(target, velocityX, velocityY);\n        return dispatchNestedPreFling(velocityX, velocityY) || tHandled;\n    }\n\n    @Override\n    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {\n        boolean tHandled = super.onNestedFling(target, velocityX, velocityY, consumed);\n        return dispatchNestedFling(velocityX, velocityY, consumed) || tHandled;\n    }\n\n    @Override\n    public boolean isNestedScrollingEnabled() {\n        return mChildHelper.isNestedScrollingEnabled();\n    }\n\n    @Override\n    public void setNestedScrollingEnabled(boolean enabled) {\n        mChildHelper.setNestedScrollingEnabled(enabled);\n    }\n\n    @Override\n    public boolean startNestedScroll(int axes) {\n        return mChildHelper.startNestedScroll(axes);\n    }\n\n    @Override\n    public void stopNestedScroll() {\n        mChildHelper.stopNestedScroll();\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent() {\n        return mChildHelper.hasNestedScrollingParent();\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,\n                                        int dyUnconsumed, int[] offsetInWindow) {\n        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed,\n                dyUnconsumed, offsetInWindow);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {\n        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);\n    }\n\n    @Override\n    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {\n        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);\n    }\n\n    @Override\n    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {\n        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/NestedScrollableHost.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL;\n\npublic class NestedScrollableHost extends FrameLayout {\n\n    private int touchSlop;\n    private float initialX = 0f;\n    private float initialY = 0f;\n\n    public NestedScrollableHost(@NonNull final Context context) {\n        this(context, null);\n    }\n\n    public NestedScrollableHost(@NonNull final Context context, @Nullable final AttributeSet attrs) {\n        super(context, attrs);\n        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(final MotionEvent ev) {\n        handleInterceptTouchEvent(ev);\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    private void handleInterceptTouchEvent(final MotionEvent e) {\n        if (getParentViewPager() == null) return;\n        final int orientation = getParentViewPager().getOrientation();\n        // Early return if child can't scroll in same direction as parent\n        if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) return;\n\n        if (e.getAction() == MotionEvent.ACTION_DOWN) {\n            initialX = e.getX();\n            initialY = e.getY();\n            getParent().requestDisallowInterceptTouchEvent(true);\n        } else if (e.getAction() == MotionEvent.ACTION_MOVE) {\n            final float dx = e.getX() - initialX;\n            final float dy = e.getY() - initialY;\n            final boolean isVpHorizontal = orientation == ORIENTATION_HORIZONTAL;\n\n            // assuming ViewPager2 touch-slop is 2x touch-slop of child\n            final float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f);\n            final float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f);\n\n            if (scaledDx > touchSlop || scaledDy > touchSlop) {\n                if (isVpHorizontal == (scaledDy > scaledDx)) {\n                    // Gesture is perpendicular, allow all parents to intercept\n                    getParent().requestDisallowInterceptTouchEvent(false);\n                } else {\n                    // Gesture is parallel, query child if movement in that direction is possible\n                    if (canChildScroll(orientation, (isVpHorizontal ? dx : dy))) {\n                        // Child can scroll, disallow all parents to intercept\n                        getParent().requestDisallowInterceptTouchEvent(true);\n                    } else {\n                        // Child cannot scroll, allow all parents to intercept\n                        getParent().requestDisallowInterceptTouchEvent(false);\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean canChildScroll(final int orientation, final float delta) {\n        final int direction = -(int) Math.signum(delta);\n        final View child = getChild();\n        if (child == null) return false;\n        ViewPager2 viewPagerChild = null;\n        if (child instanceof ViewPager2) {\n            viewPagerChild = (ViewPager2) child;\n        }\n\n        boolean canScroll;\n        switch (orientation) {\n            case 0:\n                canScroll = child.canScrollHorizontally(direction);\n                break;\n            case 1:\n                canScroll = child.canScrollVertically(direction);\n                break;\n            default:\n                throw new IllegalArgumentException();\n        }\n        if (!canScroll || viewPagerChild == null || viewPagerChild.getAdapter() == null)\n            return canScroll;\n        // check if viewpager has reached its limits and decide accordingly\n        return (direction < 0 && viewPagerChild.getCurrentItem() > 0)\n                || (direction > 0 && viewPagerChild.getCurrentItem() < viewPagerChild.getAdapter().getItemCount() - 1);\n    }\n\n    public ViewPager2 getParentViewPager() {\n        View v = (View) getParent();\n        while (v != null && !(v instanceof ViewPager2)) {\n            v = (View) v.getParent();\n        }\n        return (ViewPager2) v;\n    }\n\n    public View getChild() {\n        return getChildCount() > 0 ? getChildAt(0) : null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/PostFetcher.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.util.Log;\n\nimport java.util.List;\n\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Media;\n\npublic class PostFetcher {\n    private static final String TAG = PostFetcher.class.getSimpleName();\n\n    private final PostFetchService postFetchService;\n    private final FetchListener<List<Media>> fetchListener;\n    private boolean fetching;\n\n    public PostFetcher(final PostFetchService postFetchService,\n                       final FetchListener<List<Media>> fetchListener) {\n        this.postFetchService = postFetchService;\n        this.fetchListener = fetchListener;\n    }\n\n    public void fetch() {\n        if (fetching) return;\n        fetching = true;\n        postFetchService.fetch(new FetchListener<List<Media>>() {\n            @Override\n            public void onResult(final List<Media> result) {\n                fetching = false;\n                fetchListener.onResult(result);\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                Log.e(TAG, \"onFailure: \", t);\n            }\n        });\n    }\n\n    public void reset() {\n        postFetchService.reset();\n    }\n\n    public boolean isFetching() {\n        return fetching;\n    }\n\n    public boolean hasMore() {\n        return postFetchService.hasNextPage();\n    }\n\n    public interface PostFetchService {\n        void fetch(FetchListener<List<Media>> fetchListener);\n\n        void reset();\n\n        boolean hasNextPage();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/RecordViewAnimationHelper.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ValueAnimator;\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.os.Handler;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.AlphaAnimation;\nimport android.view.animation.Animation;\nimport android.view.animation.TranslateAnimation;\nimport android.widget.ImageView;\n\nimport androidx.appcompat.widget.AppCompatImageView;\nimport androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;\nimport androidx.vectordrawable.graphics.drawable.AnimatorInflaterCompat;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.customviews.RecordButton;\nimport awais.instagrabber.customviews.RecordView.OnBasketAnimationEnd;\n\nimport static android.view.View.INVISIBLE;\nimport static android.view.View.VISIBLE;\n\npublic class RecordViewAnimationHelper {\n    private static final String TAG = RecordViewAnimationHelper.class.getSimpleName();\n    private final Context context;\n    private final AnimatedVectorDrawableCompat animatedVectorDrawable;\n    private final ImageView basketImg;\n    private final ImageView smallBlinkingMic;\n    private AlphaAnimation alphaAnimation;\n    private OnBasketAnimationEnd onBasketAnimationEndListener;\n    private boolean isBasketAnimating;\n    private boolean isStartRecorded = false;\n    private float micX = 0;\n    private float micY = 0;\n    private AnimatorSet micAnimation;\n    private TranslateAnimation translateAnimation1, translateAnimation2;\n    private Handler handler1, handler2;\n\n    public RecordViewAnimationHelper(Context context, AppCompatImageView basketImg, AppCompatImageView smallBlinkingMic) {\n        this.context = context;\n        this.smallBlinkingMic = smallBlinkingMic;\n        this.basketImg = basketImg;\n        animatedVectorDrawable = AnimatedVectorDrawableCompat.create(context, R.drawable.recv_basket_animated);\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    public void animateBasket(float basketInitialY) {\n        isBasketAnimating = true;\n\n        clearAlphaAnimation(false);\n\n        //save initial x,y values for mic icon\n        if (micX == 0) {\n            micX = smallBlinkingMic.getX();\n            micY = smallBlinkingMic.getY();\n        }\n\n        micAnimation = (AnimatorSet) AnimatorInflaterCompat.loadAnimator(context, R.animator.delete_mic_animation);\n        micAnimation.setTarget(smallBlinkingMic); // set the view you want to animate\n\n        translateAnimation1 = new TranslateAnimation(0, 0, basketInitialY, basketInitialY - 90);\n        translateAnimation1.setDuration(250);\n\n        translateAnimation2 = new TranslateAnimation(0, 0, basketInitialY - 90, basketInitialY);\n        translateAnimation2.setDuration(350);\n\n        micAnimation.start();\n        basketImg.setImageDrawable(animatedVectorDrawable);\n\n        handler1 = new Handler();\n        handler1.postDelayed(() -> {\n            basketImg.setVisibility(VISIBLE);\n            basketImg.startAnimation(translateAnimation1);\n        }, 350);\n\n        translateAnimation1.setAnimationListener(new Animation.AnimationListener() {\n            @Override\n            public void onAnimationStart(Animation animation) {}\n\n            @Override\n            public void onAnimationEnd(Animation animation) {\n                animatedVectorDrawable.start();\n                handler2 = new Handler();\n                handler2.postDelayed(() -> {\n                    basketImg.startAnimation(translateAnimation2);\n                    smallBlinkingMic.setVisibility(INVISIBLE);\n                    basketImg.setVisibility(INVISIBLE);\n                }, 450);\n            }\n\n            @Override\n            public void onAnimationRepeat(Animation animation) {}\n        });\n\n        translateAnimation2.setAnimationListener(new Animation.AnimationListener() {\n            @Override\n            public void onAnimationStart(Animation animation) {}\n\n            @Override\n            public void onAnimationEnd(Animation animation) {\n                basketImg.setVisibility(INVISIBLE);\n                isBasketAnimating = false;\n                //if the user pressed the record button while the animation is running\n                // then do NOT call on Animation end\n                if (onBasketAnimationEndListener != null && !isStartRecorded) {\n                    onBasketAnimationEndListener.onAnimationEnd();\n                }\n            }\n\n            @Override\n            public void onAnimationRepeat(Animation animation) {}\n        });\n    }\n\n    //if the user started a new Record while the Animation is running\n    // then we want to stop the current animation and revert views back to default state\n    public void resetBasketAnimation() {\n        if (isBasketAnimating) {\n            translateAnimation1.reset();\n            translateAnimation1.cancel();\n            translateAnimation2.reset();\n            translateAnimation2.cancel();\n            micAnimation.cancel();\n            smallBlinkingMic.clearAnimation();\n            basketImg.clearAnimation();\n            if (handler1 != null) {\n                handler1.removeCallbacksAndMessages(null);\n            }\n            if (handler2 != null) {\n                handler2.removeCallbacksAndMessages(null);\n            }\n            basketImg.setVisibility(INVISIBLE);\n            smallBlinkingMic.setX(micX);\n            smallBlinkingMic.setY(micY);\n            smallBlinkingMic.setVisibility(View.GONE);\n            isBasketAnimating = false;\n        }\n    }\n\n    public void clearAlphaAnimation(boolean hideView) {\n        if (alphaAnimation != null) {\n            alphaAnimation.cancel();\n            alphaAnimation.reset();\n        }\n        smallBlinkingMic.clearAnimation();\n        if (hideView) {\n            smallBlinkingMic.setVisibility(View.GONE);\n        }\n    }\n\n    public void animateSmallMicAlpha() {\n        alphaAnimation = new AlphaAnimation(0.0f, 1.0f);\n        alphaAnimation.setDuration(500);\n        alphaAnimation.setRepeatMode(Animation.REVERSE);\n        alphaAnimation.setRepeatCount(Animation.INFINITE);\n        smallBlinkingMic.startAnimation(alphaAnimation);\n    }\n\n    public void moveRecordButtonAndSlideToCancelBack(final RecordButton recordBtn, View slideToCancelLayout, float initialX, float difX) {\n        final ValueAnimator positionAnimator = ValueAnimator.ofFloat(recordBtn.getX(), initialX);\n        positionAnimator.setInterpolator(new AccelerateDecelerateInterpolator());\n        positionAnimator.addUpdateListener(animation -> {\n            float x = (Float) animation.getAnimatedValue();\n            recordBtn.setX(x);\n        });\n        recordBtn.stopScale();\n        positionAnimator.setDuration(200);\n        positionAnimator.start();\n\n        // if the move event was not called ,then the difX will still 0 and there is no need to move it back\n        if (difX != 0) {\n            float x = initialX - difX;\n            slideToCancelLayout.animate()\n                               .x(x)\n                               .setDuration(0)\n                               .start();\n        }\n    }\n\n    public void resetSmallMic() {\n        smallBlinkingMic.setAlpha(1.0f);\n        smallBlinkingMic.setScaleX(1.0f);\n        smallBlinkingMic.setScaleY(1.0f);\n    }\n\n    public void setOnBasketAnimationEndListener(OnBasketAnimationEnd onBasketAnimationEndListener) {\n        this.onBasketAnimationEndListener = onBasketAnimationEndListener;\n\n    }\n\n    public void onAnimationEnd() {\n        if (onBasketAnimationEndListener != null) {\n            onBasketAnimationEndListener.onAnimationEnd();\n        }\n    }\n\n    //check if the user started a new Record by pressing the RecordButton\n    public void setStartRecorded(boolean startRecorded) {\n        isStartRecorded = startRecorded;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoader.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.os.Handler;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.recyclerview.widget.StaggeredGridLayoutManager;\n\nimport awais.instagrabber.interfaces.LazyLoadListener;\n\n/**\n * thanks to nesquena's <a href=\"https://gist.github.com/nesquena/d09dc68ff07e845cc622\">EndlessRecyclerViewScrollListener</a>\n */\npublic final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {\n    /**\n     * The current offset index of data you have loaded\n     */\n    private int currentPage = 0;\n    /**\n     * The total number of items in the data set after the last load\n     */\n    private int previousTotalItemCount = 0;\n    /**\n     * <code>true</code> if we are still waiting for the last set of data to load.\n     */\n    private boolean loading = true;\n    /**\n     * The minimum amount of items to have below your current scroll position before loading more.\n     */\n    private final int visibleThreshold;\n    private final LazyLoadListener lazyLoadListener;\n    private final RecyclerView.LayoutManager layoutManager;\n\n    public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,\n                              final LazyLoadListener lazyLoadListener,\n                              final int threshold) {\n        this.layoutManager = layoutManager;\n        this.lazyLoadListener = lazyLoadListener;\n        if (threshold > 0) {\n            this.visibleThreshold = threshold;\n            return;\n        }\n        if (layoutManager instanceof GridLayoutManager) {\n            this.visibleThreshold = 5 * Math.max(3, ((GridLayoutManager) layoutManager).getSpanCount());\n        } else if (layoutManager instanceof StaggeredGridLayoutManager) {\n            this.visibleThreshold = 4 * Math.max(3, ((StaggeredGridLayoutManager) layoutManager).getSpanCount());\n        } else if (layoutManager instanceof LinearLayoutManager) {\n            this.visibleThreshold = ((LinearLayoutManager) layoutManager).getReverseLayout() ? 4 : 8;\n        } else {\n            this.visibleThreshold = 5;\n        }\n    }\n\n    public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,\n                              final LazyLoadListener lazyLoadListener) {\n        this(layoutManager, lazyLoadListener, -1);\n    }\n\n    @Override\n    public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {\n        final int totalItemCount = layoutManager.getItemCount();\n\n        if (totalItemCount < previousTotalItemCount) {\n            currentPage = 0;\n            previousTotalItemCount = totalItemCount;\n            if (totalItemCount == 0) loading = true;\n        }\n\n        if (loading && totalItemCount > previousTotalItemCount) {\n            loading = false;\n            previousTotalItemCount = totalItemCount;\n        }\n\n        int lastVisibleItemPosition;\n        if (layoutManager instanceof GridLayoutManager) {\n            final GridLayoutManager layoutManager = (GridLayoutManager) this.layoutManager;\n            lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();\n        } else if (layoutManager instanceof StaggeredGridLayoutManager) {\n            final StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) this.layoutManager;\n            final int spanCount = layoutManager.getSpanCount();\n            final int[] lastVisibleItemPositions = layoutManager.findLastVisibleItemPositions(null);\n            lastVisibleItemPosition = 0;\n            for (final int itemPosition : lastVisibleItemPositions) {\n                if (itemPosition > lastVisibleItemPosition) {\n                    lastVisibleItemPosition = itemPosition;\n                }\n            }\n        } else {\n            final LinearLayoutManager layoutManager = (LinearLayoutManager) this.layoutManager;\n            lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();\n        }\n\n        if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {\n            loading = true;\n            if (lazyLoadListener != null) {\n                new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage, totalItemCount), 200);\n            }\n        }\n    }\n\n    public int getCurrentPage() {\n        return currentPage;\n    }\n\n    public void resetState() {\n        this.currentPage = 0;\n        this.previousTotalItemCount = 0;\n        this.loading = true;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoaderAtEdge.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.os.Handler;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic final class RecyclerLazyLoaderAtEdge extends RecyclerView.OnScrollListener {\n\n    private final RecyclerView.LayoutManager layoutManager;\n    private final LazyLoadListener lazyLoadListener;\n    private final boolean atTop;\n    private int currentPage;\n    private int previousItemCount;\n    private boolean loading;\n\n    public RecyclerLazyLoaderAtEdge(@NonNull final RecyclerView.LayoutManager layoutManager,\n                                    final LazyLoadListener lazyLoadListener) {\n        this.layoutManager = layoutManager;\n        this.atTop = false;\n        this.lazyLoadListener = lazyLoadListener;\n    }\n\n    public RecyclerLazyLoaderAtEdge(@NonNull final RecyclerView.LayoutManager layoutManager,\n                                    final boolean atTop,\n                                    final LazyLoadListener lazyLoadListener) {\n        this.layoutManager = layoutManager;\n        this.atTop = atTop;\n        this.lazyLoadListener = lazyLoadListener;\n    }\n\n    @Override\n    public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {\n        super.onScrollStateChanged(recyclerView, newState);\n        final int itemCount = layoutManager.getItemCount();\n        if (itemCount > previousItemCount) {\n            loading = false;\n        }\n        if (!recyclerView.canScrollVertically(atTop ? -1 : 1)\n                && newState == RecyclerView.SCROLL_STATE_IDLE\n                && !loading\n                && lazyLoadListener != null) {\n            loading = true;\n            new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage), 300);\n        }\n    }\n\n    public int getCurrentPage() {\n        return currentPage;\n    }\n\n    public void resetState() {\n        currentPage = 0;\n        previousItemCount = 0;\n        loading = true;\n    }\n\n    public interface LazyLoadListener {\n        void onLoadMore(final int page);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/RootViewDeferringInsetsCallback.java",
    "content": "package awais.instagrabber.customviews.helpers;/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.OnApplyWindowInsetsListener;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.List;\n\n/**\n * A class which extends/implements both [WindowInsetsAnimationCompat.Callback] and\n * [View.OnApplyWindowInsetsListener], which should be set on the root view in your layout.\n * <p>\n * This class enables the root view is selectively defer handling any insets which match\n * [deferredInsetTypes], to enable better looking [WindowInsetsAnimationCompat]s.\n * <p>\n * An example is the following: when a [WindowInsetsAnimationCompat] is started, the system will dispatch\n * a [WindowInsetsCompat] instance which contains the end state of the animation. For the scenario of\n * the IME being animated in, that means that the insets contains the IME height. If the view's\n * [View.OnApplyWindowInsetsListener] simply always applied the combination of\n * [WindowInsetsCompat.Type.ime] and [WindowInsetsCompat.Type.systemBars] using padding, the viewport of any\n * child views would then be smaller. This results in us animating a smaller (padded-in) view into\n * a larger viewport. Visually, this results in the views looking clipped.\n * <p>\n * This class allows us to implement a different strategy for the above scenario, by selectively\n * deferring the [WindowInsetsCompat.Type.ime] insets until the [WindowInsetsAnimationCompat] is ended.\n * For the above example, you would create a [RootViewDeferringInsetsCallback] like so:\n * <p>\n * ```\n * val callback = RootViewDeferringInsetsCallback(\n * persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),\n * deferredInsetTypes = WindowInsetsCompat.Type.ime()\n * )\n * ```\n * <p>\n * This class is not limited to just IME animations, and can work with any [WindowInsetsCompat.Type]s.\n */\npublic class RootViewDeferringInsetsCallback extends WindowInsetsAnimationCompat.Callback implements OnApplyWindowInsetsListener {\n\n    private final int persistentInsetTypes;\n    private final int deferredInsetTypes;\n    @Nullable\n    private View view = null;\n    @Nullable\n    private WindowInsetsCompat lastWindowInsets = null;\n    private boolean deferredInsets = false;\n\n    /**\n     * @param persistentInsetTypes the bitmask of any inset types which should always be handled\n     *                             through padding the attached view\n     * @param deferredInsetTypes   the bitmask of insets types which should be deferred until after\n     *                             any related [WindowInsetsAnimationCompat]s have ended\n     */\n    public RootViewDeferringInsetsCallback(final int persistentInsetTypes, final int deferredInsetTypes) {\n        super(DISPATCH_MODE_CONTINUE_ON_SUBTREE);\n        if ((persistentInsetTypes & deferredInsetTypes) != 0) {\n            throw new IllegalArgumentException(\"persistentInsetTypes and deferredInsetTypes can not contain \" +\n                                                       \"any of same WindowInsetsCompat.Type values\");\n        }\n        this.persistentInsetTypes = persistentInsetTypes;\n        this.deferredInsetTypes = deferredInsetTypes;\n    }\n\n    @Override\n    public WindowInsetsCompat onApplyWindowInsets(@NonNull final View v, @NonNull final WindowInsetsCompat windowInsets) {\n        // Store the view and insets for us in onEnd() below\n        view = v;\n        lastWindowInsets = windowInsets;\n\n        final int types = deferredInsets\n                          // When the deferred flag is enabled, we only use the systemBars() insets\n                          ? persistentInsetTypes\n                          // Otherwise we handle the combination of the the systemBars() and ime() insets\n                          : persistentInsetTypes | deferredInsetTypes;\n\n        // Finally we apply the resolved insets by setting them as padding\n        final Insets typeInsets = windowInsets.getInsets(types);\n        v.setPadding(typeInsets.left, typeInsets.top, typeInsets.right, typeInsets.bottom);\n\n        // We return the new WindowInsetsCompat.CONSUMED to stop the insets being dispatched any\n        // further into the view hierarchy. This replaces the deprecated\n        // WindowInsetsCompat.consumeSystemWindowInsets() and related functions.\n        return WindowInsetsCompat.CONSUMED;\n    }\n\n    @Override\n    public void onPrepare(WindowInsetsAnimationCompat animation) {\n        if ((animation.getTypeMask() & deferredInsetTypes) != 0) {\n            // We defer the WindowInsetsCompat.Type.ime() insets if the IME is currently not visible.\n            // This results in only the WindowInsetsCompat.Type.systemBars() being applied, allowing\n            // the scrolling view to remain at it's larger size.\n            deferredInsets = true;\n        }\n    }\n\n    @NonNull\n    @Override\n    public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets,\n                                         @NonNull final List<WindowInsetsAnimationCompat> runningAnims) {\n        // This is a no-op. We don't actually want to handle any WindowInsetsAnimations\n        return insets;\n    }\n\n    @Override\n    public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) {\n        if (deferredInsets && (animation.getTypeMask() & deferredInsetTypes) != 0) {\n            // If we deferred the IME insets and an IME animation has finished, we need to reset\n            // the flag\n            deferredInsets = false;\n\n            // And finally dispatch the deferred insets to the view now.\n            // Ideally we would just call view.requestApplyInsets() and let the normal dispatch\n            // cycle happen, but this happens too late resulting in a visual flicker.\n            // Instead we manually dispatch the most recent WindowInsets to the view.\n            if (lastWindowInsets != null && view != null) {\n                ViewCompat.dispatchApplyWindowInsets(view, lastWindowInsets);\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/SimpleImeAnimationController.java",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage awais.instagrabber.customviews.helpers;\n\nimport android.os.CancellationSignal;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationControlListenerCompat;\nimport androidx.core.view.WindowInsetsAnimationControllerCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.core.view.WindowInsetsControllerCompat;\nimport androidx.dynamicanimation.animation.FloatPropertyCompat;\nimport androidx.dynamicanimation.animation.SpringAnimation;\nimport androidx.dynamicanimation.animation.SpringForce;\n\nimport awais.instagrabber.utils.ViewUtils;\n\n/**\n * A wrapper around the [WindowInsetsAnimationControllerCompat] APIs in AndroidX Core, to simplify\n * the implementation of common use-cases around the IME.\n * <p>\n * See [InsetsAnimationLinearLayout] and [InsetsAnimationTouchListener] for examples of how\n * to use this class.\n */\npublic class SimpleImeAnimationController {\n    private static final String TAG = SimpleImeAnimationController.class.getSimpleName();\n    /**\n     * Scroll threshold for determining whether to animating to the end state, or to the start state.\n     * Currently 15% of the total swipe distance distance\n     */\n    private static final float SCROLL_THRESHOLD = 0.15f;\n\n    @Nullable\n    private WindowInsetsAnimationControllerCompat insetsAnimationController = null;\n    @Nullable\n    private CancellationSignal pendingRequestCancellationSignal = null;\n    @Nullable\n    private OnRequestReadyListener pendingRequestOnReadyListener;\n    /**\n     * True if the IME was shown at the start of the current animation.\n     */\n    private boolean isImeShownAtStart = false;\n    @Nullable\n    private SpringAnimation currentSpringAnimation = null;\n    private WindowInsetsAnimationControlListenerCompat fwdListener;\n\n    /**\n     * A LinearInterpolator instance we can re-use across listeners.\n     */\n    private final LinearInterpolator linearInterpolator = new LinearInterpolator();\n    /* To take control of the an WindowInsetsAnimation, we need to pass in a listener to\n       controlWindowInsetsAnimation() in startControlRequest(). The listener created here\n       keeps track of the current WindowInsetsAnimationController and resets our state. */\n    private final WindowInsetsAnimationControlListenerCompat animationControlListener = new WindowInsetsAnimationControlListenerCompat() {\n        /**\n         * Once the request is ready, call our [onRequestReady] function\n         */\n        @Override\n        public void onReady(@NonNull final WindowInsetsAnimationControllerCompat controller, final int types) {\n            onRequestReady(controller);\n            if (fwdListener != null) {\n                fwdListener.onReady(controller, types);\n            }\n        }\n\n        /**\n         * If the request is finished, we should reset our internal state\n         */\n        @Override\n        public void onFinished(@NonNull final WindowInsetsAnimationControllerCompat controller) {\n            reset();\n            if (fwdListener != null) {\n                fwdListener.onFinished(controller);\n            }\n        }\n\n        /**\n         * If the request is cancelled, we should reset our internal state\n         */\n        @Override\n        public void onCancelled(@Nullable final WindowInsetsAnimationControllerCompat controller) {\n            reset();\n            if (fwdListener != null) {\n                fwdListener.onCancelled(controller);\n            }\n        }\n    };\n\n    /**\n     * Start a control request to the [view]s [android.view.WindowInsetsController]. This should\n     * be called once the view is in a position to take control over the position of the IME.\n     *\n     * @param view                   The view which is triggering this request\n     * @param onRequestReadyListener optional listener which will be called when the request is ready and\n     *                               the animation can proceed\n     */\n    public void startControlRequest(@NonNull final View view,\n                                    @Nullable final OnRequestReadyListener onRequestReadyListener) {\n        if (isInsetAnimationInProgress()) {\n            Log.w(TAG, \"startControlRequest: Animation in progress. Can not start a new request to controlWindowInsetsAnimation()\");\n            return;\n        }\n\n        // Keep track of the IME insets, and the IME visibility, at the start of the request\n        final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);\n        if (rootWindowInsets != null) {\n            isImeShownAtStart = rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime());\n        }\n\n        // Create a cancellation signal, which we pass to controlWindowInsetsAnimation() below\n        pendingRequestCancellationSignal = new CancellationSignal();\n        // Keep reference to the onReady callback\n        pendingRequestOnReadyListener = onRequestReadyListener;\n\n        // Finally we make a controlWindowInsetsAnimation() request:\n        final WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(view);\n        if (windowInsetsController != null) {\n            windowInsetsController.controlWindowInsetsAnimation(\n                    // We're only catering for IME animations in this listener\n                    WindowInsetsCompat.Type.ime(),\n                    // Animation duration. This is not used by the system, and is only passed to any\n                    // WindowInsetsAnimation.Callback set on views. We pass in -1 to indicate that we're\n                    // not starting a finite animation, and that this is completely controlled by\n                    // the user's touch.\n                    -1,\n                    // The time interpolator used in calculating the animation progress. The fraction value\n                    // we passed into setInsetsAndAlpha() which be passed into this interpolator before\n                    // being used by the system to inset the IME. LinearInterpolator is a good type\n                    // to use for scrolling gestures.\n                    linearInterpolator,\n                    // A cancellation signal, which allows us to cancel the request to control\n                    pendingRequestCancellationSignal,\n                    // The WindowInsetsAnimationControlListener\n                    animationControlListener\n            );\n        }\n    }\n\n    /**\n     * Start a control request to the [view]s [android.view.WindowInsetsController], similar to\n     * [startControlRequest], but immediately fling to a finish using [velocityY] once ready.\n     * <p>\n     * This function is useful for fire-and-forget operations to animate the IME.\n     *\n     * @param view      The view which is triggering this request\n     * @param velocityY the velocity of the touch gesture which caused this call\n     */\n    public void startAndFling(@NonNull final View view, final float velocityY) {\n        startControlRequest(view, null);\n        animateToFinish(velocityY);\n    }\n\n    /**\n     * Update the inset position of the IME by the given [dy] value. This value will be coerced\n     * into the hidden and shown inset values.\n     * <p>\n     * This function should only be called if [isInsetAnimationInProgress] returns true.\n     *\n     * @return the amount of [dy] consumed by the inset animation, in pixels\n     */\n    public int insetBy(final int dy) {\n        if (insetsAnimationController == null) {\n            throw new IllegalStateException(\"Current WindowInsetsAnimationController is null.\" +\n                                                    \"This should only be called if isAnimationInProgress() returns true\");\n        }\n        final WindowInsetsAnimationControllerCompat controller = insetsAnimationController;\n\n        // Call updateInsetTo() with the new inset value\n        return insetTo(controller.getCurrentInsets().bottom - dy);\n    }\n\n    /**\n     * Update the inset position of the IME to be the given [inset] value. This value will be\n     * coerced into the hidden and shown inset values.\n     * <p>\n     * This function should only be called if [isInsetAnimationInProgress] returns true.\n     *\n     * @return the distance moved by the inset animation, in pixels\n     */\n    public int insetTo(final int inset) {\n        if (insetsAnimationController == null) {\n            throw new IllegalStateException(\"Current WindowInsetsAnimationController is null.\" +\n                                                    \"This should only be called if isAnimationInProgress() returns true\");\n        }\n        final WindowInsetsAnimationControllerCompat controller = insetsAnimationController;\n\n        final int hiddenBottom = controller.getHiddenStateInsets().bottom;\n        final int shownBottom = controller.getShownStateInsets().bottom;\n        final int startBottom = isImeShownAtStart ? shownBottom : hiddenBottom;\n        final int endBottom = isImeShownAtStart ? hiddenBottom : shownBottom;\n\n        // We coerce the given inset within the limits of the hidden and shown insets\n        final int coercedBottom = coerceIn(inset, hiddenBottom, shownBottom);\n\n        final int consumedDy = controller.getCurrentInsets().bottom - coercedBottom;\n\n        // Finally update the insets in the WindowInsetsAnimationController using\n        // setInsetsAndAlpha().\n        controller.setInsetsAndAlpha(\n                // Here we update the animating insets. This is what controls where the IME is displayed.\n                // It is also passed through to views via their WindowInsetsAnimation.Callback.\n                Insets.of(0, 0, 0, coercedBottom),\n                // This controls the alpha value. We don't want to alter the alpha so use 1f\n                1f,\n                // Finally we calculate the animation progress fraction. This value is passed through\n                // to any WindowInsetsAnimation.Callbacks, but it is not used by the system.\n                (coercedBottom - startBottom) / (float) (endBottom - startBottom)\n        );\n\n        return consumedDy;\n    }\n\n    /**\n     * Return `true` if an inset animation is in progress.\n     */\n    public boolean isInsetAnimationInProgress() {\n        return insetsAnimationController != null;\n    }\n\n    /**\n     * Return `true` if an inset animation is currently finishing.\n     */\n    public boolean isInsetAnimationFinishing() {\n        return currentSpringAnimation != null;\n    }\n\n    /**\n     * Return `true` if a request to control an inset animation is in progress.\n     */\n    public boolean isInsetAnimationRequestPending() {\n        return pendingRequestCancellationSignal != null;\n    }\n\n    /**\n     * Cancel the current [WindowInsetsAnimationControllerCompat]. We immediately finish\n     * the animation, reverting back to the state at the start of the gesture.\n     */\n    public void cancel() {\n        if (insetsAnimationController != null) {\n            insetsAnimationController.finish(isImeShownAtStart);\n        }\n        if (pendingRequestCancellationSignal != null) {\n            pendingRequestCancellationSignal.cancel();\n        }\n        if (currentSpringAnimation != null) {\n            // Cancel the current spring animation\n            currentSpringAnimation.cancel();\n        }\n        reset();\n    }\n\n    /**\n     * Finish the current [WindowInsetsAnimationControllerCompat] immediately.\n     */\n    public void finish() {\n        final WindowInsetsAnimationControllerCompat controller = insetsAnimationController;\n\n        if (controller == null) {\n            // If we don't currently have a controller, cancel any pending request and return\n            if (pendingRequestCancellationSignal != null) {\n                pendingRequestCancellationSignal.cancel();\n            }\n            return;\n        }\n\n        final int current = controller.getCurrentInsets().bottom;\n        final int shown = controller.getShownStateInsets().bottom;\n        final int hidden = controller.getHiddenStateInsets().bottom;\n\n        // The current inset matches either the shown/hidden inset, finish() immediately\n        if (current == shown) {\n            controller.finish(true);\n        } else if (current == hidden) {\n            controller.finish(false);\n        } else {\n            // Otherwise, we'll look at the current position...\n            if (controller.getCurrentFraction() >= SCROLL_THRESHOLD) {\n                // If the IME is past the 'threshold' we snap to the toggled state\n                controller.finish(!isImeShownAtStart);\n            } else {\n                // ...otherwise, we snap back to the original visibility\n                controller.finish(isImeShownAtStart);\n            }\n        }\n    }\n\n    /**\n     * Finish the current [WindowInsetsAnimationControllerCompat]. We finish the animation,\n     * animating to the end state if necessary.\n     *\n     * @param velocityY the velocity of the touch gesture which caused this call to [animateToFinish].\n     *                  Can be `null` if velocity is not available.\n     */\n    public void animateToFinish(@Nullable final Float velocityY) {\n        final WindowInsetsAnimationControllerCompat controller = insetsAnimationController;\n\n        if (controller == null) {\n            // If we don't currently have a controller, cancel any pending request and return\n            if (pendingRequestCancellationSignal != null) {\n                pendingRequestCancellationSignal.cancel();\n            }\n            return;\n        }\n\n        final int current = controller.getCurrentInsets().bottom;\n        final int shown = controller.getShownStateInsets().bottom;\n        final int hidden = controller.getHiddenStateInsets().bottom;\n\n        if (velocityY != null) {\n            // If we have a velocity, we can use it's direction to determine\n            // the visibility. Upwards == visible\n            animateImeToVisibility(velocityY > 0, velocityY);\n        } else if (current == shown) {\n            // The current inset matches either the shown/hidden inset, finish() immediately\n            controller.finish(true);\n        } else if (current == hidden) {\n            controller.finish(false);\n        } else {\n            // Otherwise, we'll look at the current position...\n            if (controller.getCurrentFraction() >= SCROLL_THRESHOLD) {\n                // If the IME is past the 'threshold' we animate it to the toggled state\n                animateImeToVisibility(!isImeShownAtStart, null);\n            } else {\n                // ...otherwise, we animate it back to the original visibility\n                animateImeToVisibility(isImeShownAtStart, null);\n            }\n        }\n    }\n\n    private void onRequestReady(@NonNull final WindowInsetsAnimationControllerCompat controller) {\n        // The request is ready, so clear out the pending cancellation signal\n        pendingRequestCancellationSignal = null;\n        // Store the current WindowInsetsAnimationController\n        insetsAnimationController = controller;\n\n        // Call any pending callback\n        if (pendingRequestOnReadyListener != null) {\n            pendingRequestOnReadyListener.onRequestReady(controller);\n        }\n        pendingRequestOnReadyListener = null;\n    }\n\n    /**\n     * Resets all of our internal state.\n     */\n    private void reset() {\n        // Clear all of our internal state\n        insetsAnimationController = null;\n        pendingRequestCancellationSignal = null;\n        isImeShownAtStart = false;\n        if (currentSpringAnimation != null) {\n            currentSpringAnimation.cancel();\n        }\n        currentSpringAnimation = null;\n        pendingRequestOnReadyListener = null;\n    }\n\n    /**\n     * Animate the IME to a given visibility.\n     *\n     * @param visible   `true` to animate the IME to it's fully shown state, `false` to it's\n     *                  fully hidden state.\n     * @param velocityY the velocity of the touch gesture which caused this call. Can be `null`\n     *                  if velocity is not available.\n     */\n    private void animateImeToVisibility(final boolean visible, @Nullable final Float velocityY) {\n        if (insetsAnimationController == null) {\n            throw new IllegalStateException(\"Controller should not be null\");\n        }\n        final WindowInsetsAnimationControllerCompat controller = insetsAnimationController;\n\n        final FloatPropertyCompat<Object> property = new FloatPropertyCompat<Object>(\"property\") {\n            @Override\n            public float getValue(final Object object) {\n                return controller.getCurrentInsets().bottom;\n            }\n\n            @Override\n            public void setValue(final Object object, final float value) {\n                if (insetsAnimationController == null) {\n                    return;\n                }\n                insetTo((int) value);\n            }\n        };\n        final float finalPosition = visible ? controller.getShownStateInsets().bottom\n                                            : controller.getHiddenStateInsets().bottom;\n        final SpringForce force = new SpringForce(finalPosition)\n                // Tweak the damping value, to remove any bounciness.\n                .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)\n                // The stiffness value controls the strength of the spring animation, which\n                // controls the speed. Medium (the default) is a good value, but feel free to\n                // play around with this value.\n                .setStiffness(SpringForce.STIFFNESS_MEDIUM);\n        ViewUtils.springAnimationOf(this, property, finalPosition)\n                 .setSpring(force)\n                 .setStartVelocity(velocityY != null ? velocityY : 0)\n                 .addEndListener((animation, canceled, value, velocity) -> {\n                     if (animation == currentSpringAnimation) {\n                         currentSpringAnimation = null;\n                     }\n                     // Once the animation has ended, finish the controller\n                     finish();\n                 }).start();\n    }\n\n    private int coerceIn(final int v, final int min, final int max) {\n        if (v >= min && v <= max) {\n            return v;\n        }\n        if (v < min) {\n            return min;\n        }\n        return max;\n    }\n\n    public void setAnimationControlListener(final WindowInsetsAnimationControlListenerCompat listener) {\n        fwdListener = listener;\n    }\n\n    public interface OnRequestReadyListener {\n        void onRequestReady(WindowInsetsAnimationControllerCompat windowInsetsAnimationControllerCompat);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/SwipeAndRestoreItemTouchHelperCallback.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.view.HapticFeedbackConstants;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.recyclerview.widget.ItemTouchHelper;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.Utils;\n\n/**\n * Thanks to https://github.com/izjumovfs/SwipeToReply/blob/master/swipetoreply/src/main/java/com/capybaralabs/swipetoreply/SwipeController.java\n */\npublic class SwipeAndRestoreItemTouchHelperCallback extends ItemTouchHelper.Callback {\n    private static final String TAG = \"SwipeRestoreCallback\";\n\n    private final float swipeThreshold;\n    private final float swipeAutoCancelThreshold;\n    private final OnSwipeListener onSwipeListener;\n    private final Drawable replyIcon;\n    // private final Drawable replyIconBackground;\n    private final int replyIconShowThreshold;\n    private final float replyIconMaxTranslation;\n    private final Rect replyIconBounds = new Rect();\n    private final float replyIconXOffset;\n    private final int replyIconSize;\n\n    private boolean mSwipeBack = false;\n    private boolean hasVibrated;\n\n    public SwipeAndRestoreItemTouchHelperCallback(final Context context, final OnSwipeListener onSwipeListener) {\n        this.onSwipeListener = onSwipeListener;\n        swipeThreshold = Utils.displayMetrics.widthPixels * 0.25f;\n        swipeAutoCancelThreshold = swipeThreshold + Utils.convertDpToPx(5);\n        replyIcon = AppCompatResources.getDrawable(context, R.drawable.ic_round_reply_24);\n        if (replyIcon == null) {\n            throw new IllegalArgumentException(\"reply icon is null\");\n        }\n        replyIcon.setTint(context.getResources().getColor(R.color.white)); //todo need to update according to theme\n        replyIconShowThreshold = Utils.convertDpToPx(24);\n        replyIconMaxTranslation = swipeThreshold - replyIconShowThreshold;\n        // Log.d(TAG, \"replyIconShowThreshold: \" + replyIconShowThreshold + \", swipeThreshold: \" + swipeThreshold);\n        replyIconSize = replyIconShowThreshold; // Utils.convertDpToPx(24);\n        replyIconXOffset = swipeThreshold * 0.25f /*Utils.convertDpToPx(20)*/;\n    }\n\n    @Override\n    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {\n        if (!(viewHolder instanceof SwipeableViewHolder)) {\n            return makeMovementFlags(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.ACTION_STATE_IDLE);\n        }\n        return makeMovementFlags(ItemTouchHelper.ACTION_STATE_IDLE, ((SwipeableViewHolder) viewHolder).getSwipeDirection());\n    }\n\n    @Override\n    public boolean onMove(@NonNull RecyclerView recyclerView,\n                          @NonNull RecyclerView.ViewHolder viewHolder,\n                          @NonNull RecyclerView.ViewHolder viewHolder1) {\n        return false;\n    }\n\n    @Override\n    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {}\n\n    @Override\n    public int convertToAbsoluteDirection(int flags, int layoutDirection) {\n        if (mSwipeBack) {\n            mSwipeBack = false;\n            return 0;\n        }\n        return super.convertToAbsoluteDirection(flags, layoutDirection);\n    }\n\n    @Override\n    public void onChildDraw(@NonNull Canvas c,\n                            @NonNull RecyclerView recyclerView,\n                            @NonNull RecyclerView.ViewHolder viewHolder,\n                            float dX,\n                            float dY,\n                            int actionState,\n                            boolean isCurrentlyActive) {\n        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {\n            setTouchListener(recyclerView, viewHolder);\n        }\n        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);\n        drawReplyButton(c, viewHolder);\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    private void setTouchListener(RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder) {\n        recyclerView.setOnTouchListener((v, event) -> {\n            if (event.getAction() == MotionEvent.ACTION_MOVE) {\n                if (Math.abs(viewHolder.itemView.getTranslationX()) >= swipeAutoCancelThreshold) {\n                    if (!hasVibrated) {\n                        viewHolder.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,\n                                                                  HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);\n                        hasVibrated = true;\n                    }\n                    //     MotionEvent cancelEvent = MotionEvent.obtain(event);\n                    //     cancelEvent.setAction(MotionEvent.ACTION_CANCEL);\n                    //     recyclerView.dispatchTouchEvent(cancelEvent);\n                    //     cancelEvent.recycle();\n                }\n            }\n            mSwipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;\n            if (mSwipeBack) {\n                hasVibrated = false;\n                if (Math.abs(viewHolder.itemView.getTranslationX()) >= swipeThreshold) {\n                    if (onSwipeListener != null) {\n                        onSwipeListener.onSwipe(viewHolder.getBindingAdapterPosition(), viewHolder);\n                    }\n                }\n            }\n            return false;\n        });\n    }\n\n    public interface SwipeableViewHolder {\n        int getSwipeDirection();\n    }\n\n    public interface OnSwipeListener {\n        void onSwipe(final int adapterPosition, final RecyclerView.ViewHolder viewHolder);\n    }\n\n    private void drawReplyButton(Canvas canvas, final RecyclerView.ViewHolder viewHolder) {\n        if (!(viewHolder instanceof SwipeableViewHolder)) return;\n        final int swipeDirection = ((SwipeableViewHolder) viewHolder).getSwipeDirection();\n        if (swipeDirection != ItemTouchHelper.START && swipeDirection != ItemTouchHelper.END) return;\n        final View view = viewHolder.itemView;\n        float translationX = view.getTranslationX();\n        boolean show = false;\n        float progress;\n        final float translationXAbs = Math.abs(translationX);\n        if (translationXAbs >= replyIconShowThreshold) {\n            show = true;\n        }\n        if (show) {\n            // replyIconShowThreshold -> swipeThreshold <=> progress 0 -> 1\n            final float replyIconTranslation = translationXAbs - replyIconShowThreshold;\n            progress = replyIconTranslation / replyIconMaxTranslation;\n            if (progress > 1) {\n                progress = 1f;\n            }\n            if (progress < 0) {\n                progress = 0;\n            }\n            // Log.d(TAG, /*\"translationX: \" + translationX + \",  replyIconTranslation: \" + replyIconTranslation +*/ \"progress: \" + progress);\n        } else {\n            progress = 0f;\n            // Log.d(TAG, /*\"translationX: \" + translationX + \",  replyIconTranslation: \" + 0 +*/ \"progress: \" + progress);\n        }\n        if (progress > 0) {\n            // calculate the reply icon y position, then offset top, bottom with icon size\n            final int y = view.getTop() + (view.getMeasuredHeight() / 2);\n            final int tempIconSize = (int) (replyIconSize * progress);\n            final int tempIconSizeHalf = tempIconSize / 2;\n            final int xOffset = (int) (replyIconXOffset * progress);\n            final int left;\n            if (swipeDirection == ItemTouchHelper.END) {\n                // draw arrow of left side\n                left = xOffset;\n            } else {\n                // draw arrow of right side\n                left = view.getMeasuredWidth() - xOffset - tempIconSize;\n            }\n            final int right = tempIconSize + left;\n            replyIconBounds.set(left, y - tempIconSizeHalf, right, y + tempIconSizeHalf);\n            replyIcon.setBounds(replyIconBounds);\n            replyIcon.draw(canvas);\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/SwipeGestureListener.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\n\nimport awais.instagrabber.interfaces.SwipeEvent;\n\npublic final class SwipeGestureListener extends GestureDetector.SimpleOnGestureListener {\n    public static final int SWIPE_THRESHOLD = 200;\n    public static final int SWIPE_VELOCITY_THRESHOLD = 200;\n    private final SwipeEvent swipeEvent;\n\n    public SwipeGestureListener(final SwipeEvent swipeEvent) {\n        this.swipeEvent = swipeEvent;\n    }\n\n    @Override\n    public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {\n        try {\n            final float diffY = e2.getY() - e1.getY();\n            final float diffX = e2.getX() - e1.getX();\n            final float diffXAbs = Math.abs(diffX);\n            if (diffXAbs > Math.abs(diffY) && diffXAbs > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {\n                if (diffX > 0) swipeEvent.onSwipe(true);\n                else swipeEvent.onSwipe(false);\n                return true;\n            }\n        } catch (final Exception e) {\n            Log.e(\"AWAISKING_APP\", \"\", e);\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/TextWatcherAdapter.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.text.Editable;\nimport android.text.TextWatcher;\n\npublic class TextWatcherAdapter implements TextWatcher {\n    @Override\n    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}\n\n    @Override\n    public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {}\n\n    @Override\n    public void afterTextChanged(final Editable s) {}\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/TranslateDeferringInsetsAnimationCallback.java",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage awais.instagrabber.customviews.helpers;\n\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsCompat;\n\nimport java.util.List;\n\n/**\n * A [WindowInsetsAnimationCompat.Callback] which will translate/move the given view during any\n * inset animations of the given inset type.\n * <p>\n * This class works in tandem with [RootViewDeferringInsetsCallback] to support the deferring of\n * certain [WindowInsetsCompat.Type] values during a [WindowInsetsAnimationCompat], provided in\n * [deferredInsetTypes]. The values passed into this constructor should match those which\n * the [RootViewDeferringInsetsCallback] is created with.\n */\npublic class TranslateDeferringInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback {\n    private final View view;\n    private final int persistentInsetTypes;\n    private final int deferredInsetTypes;\n\n    private boolean shouldTranslate = true;\n    private int kbHeight;\n\n    public TranslateDeferringInsetsAnimationCallback(final View view,\n                                                     final int persistentInsetTypes,\n                                                     final int deferredInsetTypes) {\n        this(view, persistentInsetTypes, deferredInsetTypes, DISPATCH_MODE_STOP);\n    }\n\n    /**\n     * @param view                 the view to translate from it's start to end state\n     * @param persistentInsetTypes the bitmask of any inset types which were handled as part of the\n     *                             layout\n     * @param deferredInsetTypes   the bitmask of insets types which should be deferred until after\n     *                             any [WindowInsetsAnimationCompat]s have ended\n     * @param dispatchMode         The dispatch mode for this callback.\n     *                             See [WindowInsetsAnimationCompat.Callback.getDispatchMode].\n     */\n    public TranslateDeferringInsetsAnimationCallback(final View view,\n                                                     final int persistentInsetTypes,\n                                                     final int deferredInsetTypes,\n                                                     final int dispatchMode) {\n        super(dispatchMode);\n        if ((persistentInsetTypes & deferredInsetTypes) != 0) {\n            throw new IllegalArgumentException(\"persistentInsetTypes and deferredInsetTypes can not contain \" +\n                                                       \"any of same WindowInsetsCompat.Type values\");\n        }\n        this.view = view;\n        this.persistentInsetTypes = persistentInsetTypes;\n        this.deferredInsetTypes = deferredInsetTypes;\n    }\n\n    @NonNull\n    @Override\n    public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets,\n                                         @NonNull final List<WindowInsetsAnimationCompat> runningAnimations) {\n        // onProgress() is called when any of the running animations progress...\n\n        // First we get the insets which are potentially deferred\n        final Insets typesInset = insets.getInsets(deferredInsetTypes);\n        // Then we get the persistent inset types which are applied as padding during layout\n        final Insets otherInset = insets.getInsets(persistentInsetTypes);\n\n        // Now that we subtract the two insets, to calculate the difference. We also coerce\n        // the insets to be >= 0, to make sure we don't use negative insets.\n        final Insets subtract = Insets.subtract(typesInset, otherInset);\n        final Insets diff = Insets.max(subtract, Insets.NONE);\n\n        // The resulting `diff` insets contain the values for us to apply as a translation\n        // to the view\n        view.setTranslationX(diff.left - diff.right);\n        view.setTranslationY(shouldTranslate ? diff.top - diff.bottom : -kbHeight);\n\n        return insets;\n    }\n\n    @Override\n    public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) {\n        try {\n            final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view);\n            if (kbHeight == 0) {\n                if (rootWindowInsets == null) return;\n                final Insets imeInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.ime());\n                final Insets navBarInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());\n                kbHeight = imeInsets.bottom - navBarInsets.bottom;\n            }\n            // Once the animation has ended, reset the translation values\n            view.setTranslationX(0f);\n            final boolean visible = rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime());\n            float translationY = 0;\n            if (!shouldTranslate) {\n                translationY = -kbHeight;\n                if (visible) {\n                    translationY = 0;\n                }\n            }\n            view.setTranslationY(translationY);\n        } finally {\n            shouldTranslate = true;\n        }\n    }\n\n    public void setShouldTranslate(final boolean shouldTranslate) {\n        this.shouldTranslate = shouldTranslate;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/VerticalSpaceItemDecoration.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.graphics.Rect;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {\n    private final int verticalSpaceHeight;\n\n    public VerticalSpaceItemDecoration(int verticalSpaceHeight) {\n        this.verticalSpaceHeight = verticalSpaceHeight;\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {\n        outRect.bottom = verticalSpaceHeight;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/helpers/VideoAwareRecyclerScroller.java",
    "content": "package awais.instagrabber.customviews.helpers;\n\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.exoplayer2.SimpleExoPlayer;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;\nimport awais.instagrabber.repositories.responses.Media;\n\nimport static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;\nimport static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;\n\npublic class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {\n    private static final String TAG = \"VideoAwareRecScroll\";\n    private static final int FLING_JUMP_LOW_THRESHOLD = 80;\n    private static final int FLING_JUMP_HIGH_THRESHOLD = 120;\n    private static final Object LOCK = new Object();\n\n    private LinearLayoutManager layoutManager;\n    private boolean dragging;\n    private boolean isLoadingPaused = false;\n    private FeedVideoViewHolder currentlyPlayingViewHolder;\n\n    @Override\n    public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {\n        dragging = newState == SCROLL_STATE_DRAGGING;\n        if (isLoadingPaused) {\n            if (newState == SCROLL_STATE_DRAGGING || newState == SCROLL_STATE_IDLE) {\n                // user is touchy or the scroll finished, show videos\n                isLoadingPaused = false;\n            } // settling means the user let the screen go, but it can still be flinging\n        }\n    }\n\n    @Override\n    public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {\n        if (!dragging) {\n            // TODO can be made better by a rolling average of last N calls to smooth out patterns like a,b,a\n            int currentSpeed = Math.abs(dy);\n            if (isLoadingPaused && currentSpeed < FLING_JUMP_LOW_THRESHOLD) {\n                isLoadingPaused = false;\n            } else if (!isLoadingPaused && FLING_JUMP_HIGH_THRESHOLD < currentSpeed) {\n                isLoadingPaused = true;\n                // stop playing video\n            }\n        }\n        if (isLoadingPaused) return;\n        if (layoutManager == null) {\n            final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();\n            if (layoutManager instanceof LinearLayoutManager)\n                this.layoutManager = (LinearLayoutManager) layoutManager;\n        }\n        if (layoutManager == null) {\n            return;\n        }\n        int firstVisibleItemPos = layoutManager.findFirstCompletelyVisibleItemPosition();\n        int lastVisibleItemPos = layoutManager.findLastCompletelyVisibleItemPosition();\n        if (firstVisibleItemPos == -1 && lastVisibleItemPos == -1) {\n            firstVisibleItemPos = layoutManager.findFirstVisibleItemPosition();\n            lastVisibleItemPos = layoutManager.findLastVisibleItemPosition();\n        }\n        synchronized (LOCK) {\n            final FeedVideoViewHolder videoHolder = getFirstVideoHolder(recyclerView, firstVisibleItemPos, lastVisibleItemPos);\n            if (videoHolder == null || videoHolder.getCurrentFeedModel() == null) {\n                if (currentlyPlayingViewHolder != null) {\n                    // currentlyPlayingViewHolder.stopPlaying();\n                    currentlyPlayingViewHolder = null;\n                }\n                return;\n            }\n            if (currentlyPlayingViewHolder != null && currentlyPlayingViewHolder.getCurrentFeedModel().getPk()\n                                                                                .equals(videoHolder.getCurrentFeedModel().getPk())) {\n                return;\n            }\n            if (currentlyPlayingViewHolder != null) {\n                // currentlyPlayingViewHolder.stopPlaying();\n            }\n            // videoHolder.startPlaying();\n            currentlyPlayingViewHolder = videoHolder;\n        }\n        // boolean processFirstItem = false, processLastItem = false;\n        // View currView;\n        // if (firstVisibleItemPos != -1) {\n        //     currView = layoutManager.findViewByPosition(firstVisibleItemPos);\n        //     if (currView != null && currView.getId() == R.id.videoHolder) {\n        //         firstItemView = currView;\n        //         // processFirstItem = true;\n        //     }\n        // }\n        // if (lastVisibleItemPos != -1) {\n        //     currView = layoutManager.findViewByPosition(lastVisibleItemPos);\n        //     if (currView != null && currView.getId() == R.id.videoHolder) {\n        //         lastItemView = currView;\n        //         // processLastItem = true;\n        //     }\n        // }\n        // if (firstItemView == null && lastItemView == null) {\n        //     return;\n        // }\n        // if (firstItemView != null) {\n        //\n        //     Log.d(TAG, \"view\" + viewHolder);\n        // }\n        // if (lastItemView != null) {\n        //     final FeedVideoViewHolder viewHolder = (FeedVideoViewHolder) recyclerView.getChildViewHolder(lastItemView);\n        //     Log.d(TAG, \"view\" + viewHolder);\n        // }\n        // Log.d(TAG, firstItemView + \" \" + lastItemView);\n\n        // final Rect visibleItemRect = new Rect();\n\n        // int firstVisibleItemHeight = 0, lastVisibleItemHeight = 0;\n\n        // final boolean isFirstItemVideoHolder = firstItemView != null && firstItemView.getId() == R.id.videoHolder;\n        // if (isFirstItemVideoHolder) {\n        //     firstItemView.getGlobalVisibleRect(visibleItemRect);\n        //     firstVisibleItemHeight = visibleItemRect.height();\n        // }\n        // final boolean isLastItemVideoHolder = lastItemView != null && lastItemView.getId() == R.id.videoHolder;\n        // if (isLastItemVideoHolder) {\n        //     lastItemView.getGlobalVisibleRect(visibleItemRect);\n        //     lastVisibleItemHeight = visibleItemRect.height();\n        // }\n        //\n        // if (processFirstItem && firstVisibleItemHeight > lastVisibleItemHeight)\n        //     videoPosShown = firstVisibleItemPos;\n        // else if (processLastItem && lastVisibleItemHeight != 0) videoPosShown = lastVisibleItemPos;\n        //\n        // if (firstItemView != lastItemView) {\n        //     final int mox = lastVisibleItemHeight - firstVisibleItemHeight;\n        //     if (processLastItem && lastVisibleItemHeight > firstVisibleItemHeight)\n        //         videoPosShown = lastVisibleItemPos;\n        //     if ((processFirstItem || processLastItem) && mox >= 0)\n        //         videoPosShown = lastVisibleItemPos;\n        // }\n        //\n        // if (lastChangedVideoPos != -1 && lastVideoPos != -1) {\n        //     currView = layoutManager.findViewByPosition(lastChangedVideoPos);\n        //     if (currView != null && currView.getId() == R.id.videoHolder &&\n        //             lastStoppedVideoPos != lastChangedVideoPos && lastPlayedVideoPos != lastChangedVideoPos) {\n        //         lastStoppedVideoPos = lastChangedVideoPos;\n        //         stopVideo(lastChangedVideoPos, recyclerView, currView);\n        //     }\n        //\n        //     currView = layoutManager.findViewByPosition(lastVideoPos);\n        //     if (currView != null && currView.getId() == R.id.videoHolder) {\n        //         final Rect rect = new Rect();\n        //         currView.getGlobalVisibleRect(rect);\n        //\n        //         final int holderTop = currView.getTop();\n        //         final int holderHeight = currView.getBottom() - holderTop;\n        //         final int halfHeight = holderHeight / 2;\n        //         //halfHeight -= halfHeight / 5;\n        //\n        //         if (rect.height() < halfHeight) {\n        //             if (lastStoppedVideoPos != lastVideoPos) {\n        //                 lastStoppedVideoPos = lastVideoPos;\n        //                 stopVideo(lastVideoPos, recyclerView, currView);\n        //             }\n        //         } else if (lastPlayedVideoPos != lastVideoPos) {\n        //             lastPlayedVideoPos = lastVideoPos;\n        //             playVideo(lastVideoPos, recyclerView, currView);\n        //         }\n        //     }\n        //\n        //     if (lastChangedVideoPos != lastVideoPos) lastChangedVideoPos = lastVideoPos;\n        // }\n        //\n        // if (lastVideoPos != -1 && lastVideoPos != videoPosShown) {\n        //     if (videoAttached) {\n        //         //if ((currView = layoutManager.findViewByPosition(lastVideoPos)) != null && currView.getId() == R.id.videoHolder)\n        //         releaseVideo(lastVideoPos, recyclerView, null);\n        //         videoAttached = false;\n        //     }\n        // }\n        // if (videoPosShown != -1) {\n        //     lastVideoPos = videoPosShown;\n        //     if (!videoAttached) {\n        //         if ((currView = layoutManager.findViewByPosition(videoPosShown)) != null && currView.getId() == R.id.videoHolder)\n        //             attachVideo(videoPosShown, recyclerView, currView);\n        //         videoAttached = true;\n        //     }\n        // }\n    }\n\n    private FeedVideoViewHolder getFirstVideoHolder(final RecyclerView recyclerView, final int firstVisibleItemPos, final int lastVisibleItemPos) {\n        final Rect visibleItemRect = new Rect();\n        final Point offset = new Point();\n        for (int pos = firstVisibleItemPos; pos <= lastVisibleItemPos; pos++) {\n            final View view = layoutManager.findViewByPosition(pos);\n            if (view != null && view.getId() == R.id.videoHolder) {\n                final View viewSwitcher = view.findViewById(R.id.root);\n                if (viewSwitcher == null) {\n                    continue;\n                }\n                final boolean result = viewSwitcher.getGlobalVisibleRect(visibleItemRect, offset);\n                if (!result) continue;\n                final FeedVideoViewHolder viewHolder = (FeedVideoViewHolder) recyclerView.getChildViewHolder(view);\n                final Media currentFeedModel = viewHolder.getCurrentFeedModel();\n                visibleItemRect.offset(-offset.x, -offset.y);\n                final int visibleHeight = visibleItemRect.height();\n                if (visibleHeight < currentFeedModel.getOriginalHeight()) {\n                    continue;\n                }\n                // Log.d(TAG, \"post:\" + currentFeedModel.getPostId() + \", visibleHeight: \" + visibleHeight + \", post height: \" + currentFeedModel.getImageHeight());\n                return viewHolder;\n            }\n        }\n        return null;\n    }\n\n    public void startPlaying() {\n        if (currentlyPlayingViewHolder == null) {\n            return;\n        }\n        // currentlyPlayingViewHolder.startPlaying();\n    }\n\n    public void stopPlaying() {\n        if (currentlyPlayingViewHolder == null) {\n            return;\n        }\n        // currentlyPlayingViewHolder.stopPlaying();\n    }\n\n    //     private synchronized void attachVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {\n    //         synchronized (LOCK) {\n    //             if (recyclerView != null) {\n    //                 final RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();\n    //                 if (adapter instanceof FeedAdapter) {\n    //                     final SimpleExoPlayer pagerPlayer = ((FeedAdapter) adapter).pagerPlayer;\n    //                     if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);\n    //                 }\n    //             }\n    //             if (itemView == null) {\n    //                 return;\n    //             }\n    //             final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);\n    //             final FeedModel feedModel = feedModels.get(itemPos);\n    //             // loadVideo(itemPos, itemView, shouldAutoplay, feedModel);\n    //         }\n    //     }\n    //\n    //     private void loadVideo(final int itemPos, final View itemView, final boolean shouldAutoplay, final FeedModel feedModel) {\n    //         final PlayerView playerView = itemView.findViewById(R.id.playerView);\n    //         if (playerView == null) {\n    //             return;\n    //         }\n    //         if (player != null) {\n    //             player.stop(true);\n    //             player.release();\n    //             player = null;\n    //         }\n    //\n    //         player = new SimpleExoPlayer.Builder(context)\n    //                 .setUseLazyPreparation(!shouldAutoplay)\n    //                 .build();\n    //         player.setPlayWhenReady(shouldAutoplay);\n    //\n    //         final View btnComments = itemView.findViewById(R.id.btnComments);\n    //         if (btnComments != null) {\n    //             if (feedModel.getCommentsCount() <= 0) btnComments.setEnabled(false);\n    //             else {\n    //                 btnComments.setTag(feedModel);\n    //                 btnComments.setEnabled(true);\n    //                 btnComments.setOnClickListener(commentClickListener);\n    //             }\n    //         }\n    //         playerView.setPlayer(player);\n    //         btnMute = itemView.findViewById(R.id.btnMute);\n    //         float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;\n    //         if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;\n    //         player.setVolume(vol);\n    //\n    //         if (btnMute != null) {\n    //             btnMute.setVisibility(View.VISIBLE);\n    //             btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);\n    //             btnMute.setOnClickListener(muteClickListener);\n    //         }\n    //         final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;\n    //         final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);\n    //         final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(Uri.parse(feedModel.getDisplayUrl()));\n    //\n    //         player.setRepeatMode(Player.REPEAT_MODE_ALL);\n    //         player.prepare(mediaSource);\n    //         player.setVolume(vol);\n    //\n    //         playerView.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));\n    //\n    //         if (videoChangeCallback != null) videoChangeCallback.playerChanged(itemPos, player);\n    //     }\n    //\n    //     private void releaseVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {\n    // //                    Log.d(\"AWAISKING_APP\", \"release: \" + itemPos);\n    // //         if (player != null) {\n    // //             player.stop(true);\n    // //             player.release();\n    // //         }\n    // //         player = null;\n    //     }\n    //\n    //     private void playVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {\n    // //                    if (player != null) {\n    // //                        final int playbackState = player.getPlaybackState();\n    // //                        if (!player.isPlaying()\n    // //                               || playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED\n    // //                        ) {\n    // //                            player.setPlayWhenReady(true);\n    // //                        }\n    // //                    }\n    // //                    if (player != null) {\n    // //                        player.setPlayWhenReady(true);\n    // //                        player.getPlaybackState();\n    // //                    }\n    //     }\n    //\n    //     private void stopVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {\n    //         if (player != null) {\n    //             player.setPlayWhenReady(false);\n    //             player.getPlaybackState();\n    //         }\n    //     }\n\n    public interface VideoChangeCallback {\n        void playerChanged(final int itemPos, final SimpleExoPlayer player);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/SoundParser.java",
    "content": "package awais.instagrabber.customviews.masoudss_waveform;\n\nimport android.media.MediaCodec;\nimport android.media.MediaExtractor;\nimport android.media.MediaFormat;\nimport android.nfc.FormatException;\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.ShortBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nfinal class SoundParser {\n    private ProgressListener progressListener;\n    int[] frameGains;\n    //////////////////\n    private static String[] supportedExtensions = {\"mp3\", \"wav\", \"3gpp\", \"3gp\", \"amr\", \"aac\", \"m4a\", \"ogg\"};\n    private static ArrayList<String> additionalExtensions = new ArrayList<>();\n\n    static void addCustomExtension(final String extension) {\n        additionalExtensions.add(extension);\n    }\n\n    static void removeCustomExtension(final String extension) {\n        additionalExtensions.remove(extension);\n    }\n\n    static void addCustomExtensions(final List<String> extensions) {\n        additionalExtensions.addAll(extensions);\n    }\n\n    static void removeCustomExtensions(final List<String> extensions) {\n        additionalExtensions.removeAll(extensions);\n    }\n\n    private static boolean isFilenameSupported(final String filename) {\n        for (final String supportedExtension : supportedExtensions)\n            if (filename.endsWith('.' + supportedExtension)) return true;\n        for (final String additionalExtension : additionalExtensions)\n            if (filename.endsWith('.' + additionalExtension)) return true;\n        return false;\n    }\n\n    @NonNull\n    public static SoundParser create(final String fileName, final boolean ignoreExtension) throws IOException, FormatException {\n        if (!ignoreExtension && !isFilenameSupported(fileName))\n            throw new FormatException(\"Not supported file extension.\");\n\n        final File f = new File(fileName);\n        if (!f.exists()) throw new FileNotFoundException(fileName);\n\n        final SoundParser soundFile = new SoundParser();\n        soundFile.readFile(f);\n\n        return soundFile;\n    }\n\n    public void setProgressListener(final ProgressListener progressListener) {\n        this.progressListener = progressListener;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private void readFile(@NonNull final File inputFile) throws IOException, FormatException {\n        final MediaExtractor extractor = new MediaExtractor();\n        MediaFormat format = null;\n\n        final int fileSizeBytes = (int) inputFile.length();\n        extractor.setDataSource(inputFile.getPath());\n\n        final int numTracks = extractor.getTrackCount();\n\n        int i = 0;\n        while (i < numTracks) {\n            format = extractor.getTrackFormat(i);\n            if (Objects.requireNonNull(format.getString(MediaFormat.KEY_MIME)).startsWith(\"audio/\")) {\n                extractor.selectTrack(i);\n                break;\n            }\n            i++;\n        }\n\n        if (i == numTracks) throw new FormatException(\"No audio track found in \" + inputFile);\n        assert format != null;\n\n        final int channels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);\n        final int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);\n\n        final int expectedNumSamples = (int) (format.getLong(MediaFormat.KEY_DURATION) / 1000000f * sampleRate + 0.5f);\n\n        final MediaCodec codec = MediaCodec.createDecoderByType(Objects.requireNonNull(format.getString(MediaFormat.KEY_MIME)));\n        codec.configure(format, null, null, 0);\n        codec.start();\n\n        final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();\n        final ByteBuffer[] inputBuffers = codec.getInputBuffers();\n\n        boolean firstSampleData = true, doneReading = false;\n        long presentationTime;\n        int sampleSize, decodedSamplesSize = 0, totSizeRead = 0;\n        byte[] decodedSamples = null;\n        ByteBuffer mDecodedBytes = ByteBuffer.allocate(1 << 20);\n        ByteBuffer[] outputBuffers = codec.getOutputBuffers();\n\n        while (true) {\n            final int inputBufferIndex = codec.dequeueInputBuffer(100);\n\n            if (!doneReading && inputBufferIndex >= 0) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)\n                    sampleSize = extractor.readSampleData(Objects.requireNonNull(codec.getInputBuffer(inputBufferIndex)), 0);\n                else\n                    sampleSize = extractor.readSampleData(inputBuffers[inputBufferIndex], 0);\n\n                if (firstSampleData && sampleSize == 2 && \"audio/mp4a-latm\".equals(format.getString(MediaFormat.KEY_MIME))) {\n                    extractor.advance();\n                    totSizeRead += sampleSize;\n                } else if (sampleSize < 0) {\n                    codec.queueInputBuffer(inputBufferIndex, 0, 0, -1, MediaCodec.BUFFER_FLAG_END_OF_STREAM);\n                    doneReading = true;\n                } else {\n                    presentationTime = extractor.getSampleTime();\n                    codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTime, 0);\n                    extractor.advance();\n                    totSizeRead += sampleSize;\n\n                    if (progressListener != null && !progressListener.reportProgress((double) totSizeRead / fileSizeBytes)) {\n                        // We are asked to stop reading the file. Returning immediately.\n                        // The SoundFile object is invalid and should NOT be used afterward!\n                        extractor.release();\n                        codec.stop();\n                        codec.release();\n                        return;\n                    }\n                }\n\n                firstSampleData = false;\n            }\n\n            // Get decoded stream from the decoder output buffers.\n            final int outputBufferIndex = codec.dequeueOutputBuffer(info, 100);\n            if (outputBufferIndex >= 0 && info.size > 0) {\n                if (decodedSamplesSize < info.size) {\n                    decodedSamplesSize = info.size;\n                    decodedSamples = new byte[decodedSamplesSize];\n                }\n\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                    final ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferIndex);\n                    assert outputBuffer != null;\n                    outputBuffer.get(decodedSamples, 0, info.size);\n                    outputBuffer.clear();\n                } else {\n                    outputBuffers[outputBufferIndex].get(decodedSamples, 0, info.size);\n                    outputBuffers[outputBufferIndex].clear();\n                }\n\n                // Check if buffer is big enough. Resize it if it's too small.\n                if (mDecodedBytes.remaining() < info.size) {\n                    // Getting a rough estimate of the total size, allocate 20% more, and\n                    // make sure to allocate at least 5MB more than the initial size.\n                    final int position = mDecodedBytes.position();\n\n                    int newSize = (int) (position * (1.0 * fileSizeBytes / totSizeRead) * 1.2);\n                    final int infoSize = info.size + 5 * (1 << 20);\n                    if (newSize - position < infoSize)\n                        newSize = position + infoSize;\n\n                    ByteBuffer newDecodedBytes = null;\n\n                    // Try to allocate memory. If we are OOM, try to run the garbage collector.\n                    int retry = 10;\n                    while (retry > 0) {\n                        try {\n                            newDecodedBytes = ByteBuffer.allocate(newSize);\n                            break;\n                        } catch (final OutOfMemoryError e) {\n                            retry--;\n                        }\n                    }\n                    if (retry == 0) break;\n                    mDecodedBytes.rewind();\n                    assert newDecodedBytes != null;\n                    newDecodedBytes.put(mDecodedBytes);\n                    mDecodedBytes = newDecodedBytes;\n                    mDecodedBytes.position(position);\n                }\n\n                mDecodedBytes.put(decodedSamples, 0, info.size);\n                codec.releaseOutputBuffer(outputBufferIndex, false);\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)\n                    outputBuffers = codec.getOutputBuffers();\n            }\n\n            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 || mDecodedBytes.position() / (2 * channels) >= expectedNumSamples)\n                break;\n        }\n\n        final int numSamples = mDecodedBytes.position() / (channels * 2);  // One sample = 2 bytes.\n        mDecodedBytes.rewind();\n        mDecodedBytes.order(ByteOrder.LITTLE_ENDIAN);\n        final ShortBuffer mDecodedSamples = mDecodedBytes.asShortBuffer();\n        // final int avgBitrateKbps = (int) (fileSizeBytes * 8F * ((float) sampleRate / numSamples) / 1000F);\n\n        extractor.release();\n        codec.stop();\n        codec.release();\n\n        final int samplesPerFrame = 1024;\n        int numFrames = numSamples / samplesPerFrame;\n        if (numSamples % samplesPerFrame != 0) numFrames++;\n        frameGains = new int[numFrames];\n        // final int[] mFrameLens = new int[numFrames];\n        // final int[] mFrameOffsets = new int[numFrames];\n        // final int frameLens = (int) (1000F * avgBitrateKbps / 8F * ((float) samplesPerFrame / sampleRate));\n        int j, gain, value;\n\n        i = 0;\n        while (i < numFrames) {\n            gain = -1;\n            j = 0;\n\n            while (j < samplesPerFrame) {\n                value = 0;\n                for (int k = 0; k < channels; ++k)\n                    if (mDecodedSamples.remaining() > 0)\n                        value += Math.abs(mDecodedSamples.get());\n                value /= channels;\n                if (gain < value) gain = value;\n                j++;\n            }\n\n            frameGains[i] = (int) Math.sqrt(gain);\n            // mFrameLens[i] = frameLens;\n            // mFrameOffsets[i] = (int) ((float) i * (1000F * avgBitrateKbps / 8F) * ((float) samplesPerFrame / sampleRate));\n            i++;\n        }\n\n        mDecodedSamples.rewind();\n    }\n\n    private interface ProgressListener {\n        boolean reportProgress(final double fractionComplete);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveFormProgressChangeListener.java",
    "content": "package awais.instagrabber.customviews.masoudss_waveform;\n\npublic interface WaveFormProgressChangeListener {\n    void onProgressChanged(final WaveformSeekBar waveformSeekBar, final int progress, final boolean fromUser);\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveGravity.java",
    "content": "package awais.instagrabber.customviews.masoudss_waveform;\n\npublic enum WaveGravity {\n    TOP,\n    CENTER,\n    BOTTOM,\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java",
    "content": "package awais.instagrabber.customviews.masoudss_waveform;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapShader;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.graphics.Shader;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.CubicInterpolation;\nimport awais.instagrabber.utils.Utils;\n\npublic final class WaveformSeekBar extends View {\n    private final int mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();\n    private final Paint mWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private final RectF mWaveRect = new RectF();\n    private final Canvas mProgressCanvas = new Canvas();\n    private final WaveGravity waveGravity = WaveGravity.CENTER;\n    private final int waveBackgroundColor;\n    private final int waveProgressColor;\n    private final float waveWidth = Utils.convertDpToPx(3);\n    private final float waveMinHeight = Utils.convertDpToPx(4);\n    private final float waveCornerRadius = Utils.convertDpToPx(2);\n    private final float waveGap = Utils.convertDpToPx(1);\n    // private int mCanvasWidth = 0;\n    // private int mCanvasHeight = 0;\n    private float mTouchDownX = 0F;\n    private float[] sample;\n    private int progress = 0;\n    private WaveFormProgressChangeListener progressChangeListener;\n    private int wavesCount;\n    private CubicInterpolation interpolation;\n\n    public WaveformSeekBar(final Context context) {\n        this(context, null);\n    }\n\n    public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        final TypedArray a = context.getTheme().obtainStyledAttributes(\n                attrs,\n                R.styleable.WaveformSeekBar,\n                0,\n                0);\n        final int backgroundColor;\n        final int progressColor;\n        try {\n            backgroundColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformBackgroundColor, R.color.white);\n            progressColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformProgressColor, R.color.blue_800);\n        } finally {\n            a.recycle();\n        }\n        this.waveBackgroundColor = context.getResources().getColor(backgroundColor);\n        this.waveProgressColor = context.getResources().getColor(progressColor);\n    }\n\n    private float getSampleMax() {\n        float max = -1f;\n        if (sample != null) {\n            for (final float v : sample) {\n                if (v > max) max = v;\n            }\n        }\n        return max;\n    }\n\n    @Override\n    protected void onDraw(final Canvas canvas) {\n        super.onDraw(canvas);\n        if (sample == null || sample.length == 0) return;\n        final int availableWidth = getAvailableWidth();\n        final int availableHeight = getAvailableHeight();\n\n        // final float step = availableWidth / (waveGap + waveWidth) / sample.size();\n\n        int i = 0;\n        float lastWaveRight = (float) getPaddingLeft();\n\n        final float sampleMax = getSampleMax();\n        while (i < wavesCount) {\n            final float t = lastWaveRight / availableWidth * sample.length;\n            float waveHeight = availableHeight * (interpolation.interpolate(t) / sampleMax);\n\n            if (waveHeight < waveMinHeight)\n                waveHeight = waveMinHeight;\n\n            final float top;\n            if (waveGravity == WaveGravity.TOP) {\n                top = (float) getPaddingTop();\n            } else if (waveGravity == WaveGravity.CENTER) {\n                top = (float) getPaddingTop() + availableHeight / 2F - waveHeight / 2F;\n            } else if (waveGravity == WaveGravity.BOTTOM) {\n                top = getMeasuredHeight() - (float) getPaddingBottom() - waveHeight;\n            } else {\n                top = 0;\n            }\n\n            mWaveRect.set(lastWaveRight, top, lastWaveRight + waveWidth, top + waveHeight);\n\n            if (mWaveRect.contains(availableWidth * progress / 100F, mWaveRect.centerY())) {\n                int bitHeight = (int) mWaveRect.height();\n                if (bitHeight <= 0) bitHeight = (int) waveWidth;\n\n                final Bitmap bitmap = Bitmap.createBitmap(availableWidth, bitHeight, Bitmap.Config.ARGB_8888);\n                mProgressCanvas.setBitmap(bitmap);\n\n                float fillWidth = availableWidth * progress / 100F;\n\n                mWavePaint.setColor(waveProgressColor);\n                mProgressCanvas.drawRect(0F, 0F, fillWidth, mWaveRect.bottom, mWavePaint);\n\n                mWavePaint.setColor(waveBackgroundColor);\n                mProgressCanvas.drawRect(fillWidth, 0F, (float) availableWidth, mWaveRect.bottom, mWavePaint);\n\n                mWavePaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));\n            } else {\n                mWavePaint.setColor(mWaveRect.right <= availableWidth * progress / 100F ? waveProgressColor : waveBackgroundColor);\n                mWavePaint.setShader(null);\n            }\n\n            canvas.drawRoundRect(mWaveRect, waveCornerRadius, waveCornerRadius, mWavePaint);\n\n            lastWaveRight = mWaveRect.right + waveGap;\n\n            if (lastWaveRight + waveWidth > availableWidth + getPaddingLeft()) {\n                break;\n            }\n            i++;\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(final MotionEvent event) {\n        if (!isEnabled()) return false;\n\n        switch (event.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                if (isParentScrolling()) mTouchDownX = event.getX();\n                else updateProgress(event);\n                break;\n\n            case MotionEvent.ACTION_MOVE:\n                updateProgress(event);\n                break;\n\n            case MotionEvent.ACTION_UP:\n                if (Math.abs(event.getX() - mTouchDownX) > mScaledTouchSlop)\n                    updateProgress(event);\n\n                performClick();\n                break;\n        }\n\n        return true;\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (changed) {\n            calculateWaveDimensions();\n        }\n    }\n\n    private void calculateWaveDimensions() {\n        if (sample == null || sample.length == 0) return;\n        final int availableWidth = getAvailableWidth();\n        wavesCount = (int) (availableWidth / (waveGap + waveWidth));\n        interpolation = new CubicInterpolation(sample);\n    }\n\n    // @Override\n    // protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {\n    //     super.onSizeChanged(w, h, oldw, oldh);\n    //     mCanvasWidth = w;\n    //     mCanvasHeight = h;\n    // }\n\n    @Override\n    public boolean performClick() {\n        super.performClick();\n        return true;\n    }\n\n    private boolean isParentScrolling() {\n        View parent = (View) getParent();\n        final View root = getRootView();\n\n        while (true) {\n            if (parent.canScrollHorizontally(1) || parent.canScrollHorizontally(-1) ||\n                    parent.canScrollVertically(1) || parent.canScrollVertically(-1))\n                return true;\n\n            if (parent == root) return false;\n\n            parent = (View) parent.getParent();\n        }\n    }\n\n    private void updateProgress(@NonNull final MotionEvent event) {\n        progress = (int) (100 * event.getX() / getAvailableWidth());\n        invalidate();\n\n        if (progressChangeListener != null)\n            progressChangeListener.onProgressChanged(this, Math.min(Math.max(0, progress), 100), true);\n    }\n\n    private int getAvailableWidth() {\n        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\n    }\n\n    private int getAvailableHeight() {\n        return getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\n    }\n\n    public void setProgress(final int progress) {\n        this.progress = progress;\n        invalidate();\n    }\n\n    public void setProgressChangeListener(final WaveFormProgressChangeListener progressChangeListener) {\n        this.progressChangeListener = progressChangeListener;\n    }\n\n    public void setSample(final float[] sample) {\n        if (sample == this.sample) return;\n        this.sample = sample;\n        calculateWaveDimensions();\n        invalidate();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/AppDatabase.kt",
    "content": "package awais.instagrabber.db\n\nimport android.content.ContentValues\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport android.util.Log\nimport androidx.room.Database\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\nimport androidx.room.TypeConverters\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\nimport awais.instagrabber.db.dao.AccountDao\nimport awais.instagrabber.db.dao.DMLastNotifiedDao\nimport awais.instagrabber.db.dao.FavoriteDao\nimport awais.instagrabber.db.dao.RecentSearchDao\nimport awais.instagrabber.db.entities.Account\nimport awais.instagrabber.db.entities.DMLastNotified\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneId\nimport java.util.*\n\n@Database(entities = [Account::class, Favorite::class, DMLastNotified::class, RecentSearch::class], version = 6)\n@TypeConverters(Converters::class)\nabstract class AppDatabase : RoomDatabase() {\n    abstract fun accountDao(): AccountDao\n    abstract fun favoriteDao(): FavoriteDao\n    abstract fun dmLastNotifiedDao(): DMLastNotifiedDao\n    abstract fun recentSearchDao(): RecentSearchDao\n\n    companion object {\n        private lateinit var INSTANCE: AppDatabase\n\n        fun getDatabase(context: Context): AppDatabase {\n            if (!this::INSTANCE.isInitialized) {\n                synchronized(AppDatabase::class.java) {\n                    if (!this::INSTANCE.isInitialized) {\n                        INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, \"cookiebox.db\")\n                            .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)\n                            .build()\n                    }\n                }\n            }\n            return INSTANCE\n        }\n\n        private val MIGRATION_1_2: Migration = object : Migration(1, 2) {\n            override fun migrate(db: SupportSQLiteDatabase) {\n                db.execSQL(\"ALTER TABLE cookies ADD \" + Account.COL_FULL_NAME + \" TEXT\")\n                db.execSQL(\"ALTER TABLE cookies ADD \" + Account.COL_PROFILE_PIC + \" TEXT\")\n            }\n        }\n        private val MIGRATION_2_3: Migration = object : Migration(2, 3) {\n            override fun migrate(db: SupportSQLiteDatabase) {\n                val oldFavorites = backupOldFavorites(db)\n                // recreate with new columns (as there will be no doubt about the `query_display` column being present or not in the future versions)\n                db.execSQL(\"DROP TABLE \" + Favorite.TABLE_NAME)\n                db.execSQL(\"CREATE TABLE \" + Favorite.TABLE_NAME + \" (\"\n                           + Favorite.COL_ID + \" INTEGER PRIMARY KEY,\"\n                           + Favorite.COL_QUERY + \" TEXT,\"\n                           + Favorite.COL_TYPE + \" TEXT,\"\n                           + Favorite.COL_DISPLAY_NAME + \" TEXT,\"\n                           + Favorite.COL_PIC_URL + \" TEXT,\"\n                           + Favorite.COL_DATE_ADDED + \" INTEGER)\")\n                // add the old favorites back\n                for (oldFavorite in oldFavorites) {\n                    insertOrUpdateFavorite(db, oldFavorite)\n                }\n            }\n        }\n        private val MIGRATION_3_4: Migration = object : Migration(3, 4) {\n            override fun migrate(db: SupportSQLiteDatabase) {\n                // Required when migrating to Room.\n                // The original table primary keys were not 'NOT NULL', so the migration to Room were failing without the below migration.\n                // Taking this opportunity to rename cookies table to accounts\n\n                // Create new table with name 'accounts'\n                db.execSQL(\"CREATE TABLE \" + Account.TABLE_NAME + \" (\"\n                           + Account.COL_ID + \" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"\n                           + Account.COL_UID + \" TEXT,\"\n                           + Account.COL_USERNAME + \" TEXT,\"\n                           + Account.COL_COOKIE + \" TEXT,\"\n                           + Account.COL_FULL_NAME + \" TEXT,\"\n                           + Account.COL_PROFILE_PIC + \" TEXT)\")\n                // Insert all data from table 'cookies' to 'accounts'\n                db.execSQL(\"INSERT INTO \" + Account.TABLE_NAME + \" (\"\n                           + Account.COL_UID + \",\"\n                           + Account.COL_USERNAME + \",\"\n                           + Account.COL_COOKIE + \",\"\n                           + Account.COL_FULL_NAME + \",\"\n                           + Account.COL_PROFILE_PIC + \") \"\n                           + \"SELECT \"\n                           + Account.COL_UID + \",\"\n                           + Account.COL_USERNAME + \",\"\n                           + Account.COL_COOKIE + \",\"\n                           + Account.COL_FULL_NAME + \",\"\n                           + Account.COL_PROFILE_PIC\n                           + \" FROM cookies\")\n                // Drop old cookies table\n                db.execSQL(\"DROP TABLE cookies\")\n\n                // Create favorite backup table\n                db.execSQL(\"CREATE TABLE \" + Favorite.TABLE_NAME + \"_backup (\"\n                           + Favorite.COL_ID + \" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"\n                           + Favorite.COL_QUERY + \" TEXT,\"\n                           + Favorite.COL_TYPE + \" TEXT,\"\n                           + Favorite.COL_DISPLAY_NAME + \" TEXT,\"\n                           + Favorite.COL_PIC_URL + \" TEXT,\"\n                           + Favorite.COL_DATE_ADDED + \" INTEGER)\")\n                // Insert all data from table 'favorite' to 'favorite_backup'\n                db.execSQL(\"INSERT INTO \" + Favorite.TABLE_NAME + \"_backup (\"\n                           + Favorite.COL_QUERY + \",\"\n                           + Favorite.COL_TYPE + \",\"\n                           + Favorite.COL_DISPLAY_NAME + \",\"\n                           + Favorite.COL_PIC_URL + \",\"\n                           + Favorite.COL_DATE_ADDED + \") \"\n                           + \"SELECT \"\n                           + Favorite.COL_QUERY + \",\"\n                           + Favorite.COL_TYPE + \",\"\n                           + Favorite.COL_DISPLAY_NAME + \",\"\n                           + Favorite.COL_PIC_URL + \",\"\n                           + Favorite.COL_DATE_ADDED\n                           + \" FROM \" + Favorite.TABLE_NAME)\n                // Drop favorites\n                db.execSQL(\"DROP TABLE \" + Favorite.TABLE_NAME)\n                // Rename favorite_backup to favorites\n                db.execSQL(\"ALTER TABLE \" + Favorite.TABLE_NAME + \"_backup RENAME TO \" + Favorite.TABLE_NAME)\n            }\n        }\n\n        @JvmField\n        val MIGRATION_4_5: Migration = object : Migration(4, 5) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"CREATE TABLE IF NOT EXISTS `dm_last_notified` (\" +\n                                 \"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \" +\n                                 \"`thread_id` TEXT, \" +\n                                 \"`last_notified_msg_ts` INTEGER, \" +\n                                 \"`last_notified_at` INTEGER)\")\n                database.execSQL(\"CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `dm_last_notified` (`thread_id`)\")\n            }\n        }\n\n        @JvmField\n        val MIGRATION_5_6: Migration = object : Migration(5, 6) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"CREATE TABLE IF NOT EXISTS `recent_searches` (\" +\n                                 \"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \" +\n                                 \"`ig_id` TEXT NOT NULL, \" +\n                                 \"`name` TEXT NOT NULL, \" +\n                                 \"`username` TEXT, \" +\n                                 \"`pic_url` TEXT, \" +\n                                 \"`type` TEXT NOT NULL, \" +\n                                 \"`last_searched_on` INTEGER NOT NULL)\")\n                database.execSQL(\"CREATE UNIQUE INDEX IF NOT EXISTS `index_recent_searches_ig_id_type` ON `recent_searches` (`ig_id`, `type`)\")\n            }\n        }\n\n        private fun backupOldFavorites(db: SupportSQLiteDatabase): List<Favorite> {\n            // check if old favorites table had the column query_display\n            val queryDisplayExists = checkColumnExists(db, Favorite.TABLE_NAME, \"query_display\")\n            Log.d(TAG, \"backupOldFavorites: queryDisplayExists: $queryDisplayExists\")\n            val oldModels: MutableList<Favorite> = ArrayList()\n            val sql = (\"SELECT \"\n                       + \"query_text,\"\n                       + \"date_added\"\n                       + (if (queryDisplayExists) \",query_display\" else \"\")\n                       + \" FROM \" + Favorite.TABLE_NAME)\n            try {\n                db.query(sql).use { cursor ->\n                    if (cursor != null && cursor.moveToFirst()) {\n                        do {\n                            try {\n                                val queryText = cursor.getString(cursor.getColumnIndex(\"query_text\"))\n                                val favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText) ?: continue\n                                val type = favoriteTypeQueryPair.first\n                                val query = favoriteTypeQueryPair.second\n                                val epochMillis = cursor.getLong(cursor.getColumnIndex(\"date_added\"))\n                                val localDateTime = LocalDateTime.ofInstant(\n                                    Instant.ofEpochMilli(epochMillis),\n                                    ZoneId.systemDefault()\n                                )\n                                oldModels.add(Favorite(\n                                    0,\n                                    query,\n                                    type,\n                                    if (queryDisplayExists) cursor.getString(cursor.getColumnIndex(\"query_display\")) else null,\n                                    null,\n                                    localDateTime\n                                ))\n                            } catch (e: Exception) {\n                                Log.e(TAG, \"onUpgrade\", e)\n                            }\n                        } while (cursor.moveToNext())\n                    }\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"onUpgrade\", e)\n            }\n            Log.d(TAG, \"backupOldFavorites: oldModels:$oldModels\")\n            return oldModels\n        }\n\n        @Synchronized\n        private fun insertOrUpdateFavorite(db: SupportSQLiteDatabase, model: Favorite) {\n            val values = ContentValues()\n            values.put(Favorite.COL_QUERY, model.query)\n            values.put(Favorite.COL_TYPE, model.type.toString())\n            values.put(Favorite.COL_DISPLAY_NAME, model.displayName)\n            values.put(Favorite.COL_PIC_URL, model.picUrl)\n            values.put(Favorite.COL_DATE_ADDED, model.dateAdded!!.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())\n            val rows: Int = if (model.id >= 1) {\n                db.update(Favorite.TABLE_NAME,\n                    SQLiteDatabase.CONFLICT_IGNORE,\n                    values,\n                    Favorite.COL_ID + \"=?\", arrayOf(model.id.toString()))\n            } else {\n                db.update(Favorite.TABLE_NAME,\n                    SQLiteDatabase.CONFLICT_IGNORE,\n                    values,\n                    Favorite.COL_QUERY + \"=?\" + \" AND \" + Favorite.COL_TYPE + \"=?\", arrayOf(model.query, model.type.toString()))\n            }\n            if (rows != 1) {\n                db.insert(Favorite.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values)\n            }\n        }\n\n        @Suppress(\"SameParameterValue\")\n        private fun checkColumnExists(\n            db: SupportSQLiteDatabase,\n            tableName: String,\n            columnName: String,\n        ): Boolean {\n            var exists = false\n            try {\n                db.query(\"PRAGMA table_info($tableName)\").use { cursor ->\n                    if (cursor.moveToFirst()) {\n                        do {\n                            val currentColumn = cursor.getString(cursor.getColumnIndex(\"name\"))\n                            if (currentColumn == columnName) {\n                                exists = true\n                            }\n                        } while (cursor.moveToNext())\n                    }\n                }\n            } catch (ex: Exception) {\n                Log.e(TAG, \"checkColumnExists\", ex)\n            }\n            return exists\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/Converters.kt",
    "content": "package awais.instagrabber.db\n\nimport androidx.room.TypeConverter\nimport awais.instagrabber.models.enums.FavoriteType\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneId\nimport java.time.ZoneOffset\n\nclass Converters {\n    @TypeConverter\n    fun fromFavoriteTypeString(value: String?): FavoriteType? =\n        if (value == null) null\n        else try {\n            FavoriteType.valueOf(value)\n        } catch (e: Exception) {\n            null\n        }\n\n    @TypeConverter\n    fun favoriteTypeToString(favoriteType: FavoriteType?): String? = favoriteType?.toString()\n\n    @TypeConverter\n    fun fromTimestampToLocalDateTime(value: Long?): LocalDateTime? =\n        if (value == null) null else LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.systemDefault())\n\n    @TypeConverter\n    fun localDateTimeToTimestamp(localDateTime: LocalDateTime?): Long? = localDateTime?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/dao/AccountDao.kt",
    "content": "package awais.instagrabber.db.dao\n\nimport androidx.room.*\nimport awais.instagrabber.db.entities.Account\n\n@Dao\ninterface AccountDao {\n    @Query(\"SELECT * FROM accounts\")\n    suspend fun getAllAccounts(): List<Account>\n\n    @Query(\"SELECT * FROM accounts WHERE uid = :uid\")\n    suspend fun findAccountByUid(uid: String): Account?\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertAccounts(vararg accounts: Account)\n\n    @Update\n    suspend fun updateAccounts(vararg accounts: Account)\n\n    @Delete\n    suspend fun deleteAccounts(vararg accounts: Account)\n\n    @Query(\"DELETE from accounts\")\n    suspend fun deleteAllAccounts()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/dao/DMLastNotifiedDao.kt",
    "content": "package awais.instagrabber.db.dao\n\nimport androidx.room.*\nimport awais.instagrabber.db.entities.DMLastNotified\n\n@Dao\ninterface DMLastNotifiedDao {\n    @Query(\"SELECT * FROM dm_last_notified\")\n    suspend fun getAllDMDmLastNotified(): List<DMLastNotified>\n\n    @Query(\"SELECT * FROM dm_last_notified WHERE thread_id = :threadId\")\n    suspend fun findDMLastNotifiedByThreadId(threadId: String): DMLastNotified?\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    suspend fun insertDMLastNotified(vararg dmLastNotified: DMLastNotified)\n\n    @Update\n    suspend fun updateDMLastNotified(vararg dmLastNotified: DMLastNotified)\n\n    @Delete\n    suspend fun deleteDMLastNotified(vararg dmLastNotified: DMLastNotified)\n\n    @Query(\"DELETE from dm_last_notified\")\n    suspend fun deleteAllDMLastNotified()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/dao/FavoriteDao.kt",
    "content": "package awais.instagrabber.db.dao\n\nimport androidx.room.*\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.models.enums.FavoriteType\n\n@Dao\ninterface FavoriteDao {\n    @Query(\"SELECT * FROM favorites\")\n    suspend fun getAllFavorites(): List<Favorite>\n\n    @Query(\"SELECT * FROM favorites WHERE query_text = :query and type = :type\")\n    suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite?\n\n    @Insert\n    suspend fun insertFavorites(vararg favorites: Favorite)\n\n    @Update\n    suspend fun updateFavorites(vararg favorites: Favorite)\n\n    @Delete\n    suspend fun deleteFavorites(vararg favorites: Favorite)\n\n    @Query(\"DELETE from favorites\")\n    suspend fun deleteAllFavorites()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/dao/RecentSearchDao.kt",
    "content": "package awais.instagrabber.db.dao\n\nimport androidx.room.*\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.models.enums.FavoriteType\n\n@Dao\ninterface RecentSearchDao {\n    @Query(\"SELECT * FROM recent_searches ORDER BY last_searched_on DESC\")\n    suspend fun getAllRecentSearches(): List<RecentSearch>\n\n    @Query(\"SELECT * FROM recent_searches WHERE `ig_id` = :igId AND `type` = :type\")\n    suspend fun getRecentSearchByIgIdAndType(igId: String, type: FavoriteType): RecentSearch?\n\n    @Query(\"SELECT * FROM recent_searches WHERE instr(`name`, :query) > 0\")\n    suspend fun findRecentSearchesWithNameContaining(query: String): List<RecentSearch>\n\n    @Insert\n    suspend fun insertRecentSearch(recentSearch: RecentSearch)\n\n    @Update\n    suspend fun updateRecentSearch(recentSearch: RecentSearch)\n\n    @Delete\n    suspend fun deleteRecentSearch(recentSearch: RecentSearch)\n\n    // @Query(\"DELETE from recent_searches\")\n    // void deleteAllRecentSearches();\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/datasources/AccountDataSource.kt",
    "content": "package awais.instagrabber.db.datasources\n\nimport android.content.Context\nimport awais.instagrabber.db.AppDatabase\nimport awais.instagrabber.db.dao.AccountDao\nimport awais.instagrabber.db.entities.Account\n\nclass AccountDataSource(private val accountDao: AccountDao) {\n    suspend fun getAccount(uid: String): Account? = accountDao.findAccountByUid(uid)\n\n    suspend fun getAllAccounts(): List<Account> = accountDao.getAllAccounts()\n\n    suspend fun insertOrUpdateAccount(\n        uid: String?,\n        username: String?,\n        cookie: String?,\n        fullName: String?,\n        profilePicUrl: String?,\n    ) {\n        val account = uid?.let { getAccount(it) }\n        val toUpdate = Account(account?.id ?: 0, uid, username, cookie, fullName, profilePicUrl)\n        if (account != null) {\n            accountDao.updateAccounts(toUpdate)\n            return\n        }\n        accountDao.insertAccounts(toUpdate)\n    }\n\n    suspend fun deleteAccount(account: Account) = accountDao.deleteAccounts(account)\n\n    suspend fun deleteAllAccounts() = accountDao.deleteAllAccounts()\n\n    companion object {\n        @Volatile\n        private var INSTANCE: AccountDataSource? = null\n\n        fun getInstance(context: Context): AccountDataSource {\n            return INSTANCE ?: synchronized(this) {\n                val dao: AccountDao = AppDatabase.getDatabase(context).accountDao()\n                AccountDataSource(dao).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/datasources/DMLastNotifiedDataSource.kt",
    "content": "package awais.instagrabber.db.datasources\n\nimport android.content.Context\nimport awais.instagrabber.db.AppDatabase\nimport awais.instagrabber.db.dao.DMLastNotifiedDao\nimport awais.instagrabber.db.entities.DMLastNotified\nimport java.time.LocalDateTime\n\nclass DMLastNotifiedDataSource private constructor(private val dmLastNotifiedDao: DMLastNotifiedDao) {\n    suspend fun getDMLastNotified(threadId: String): DMLastNotified? = dmLastNotifiedDao.findDMLastNotifiedByThreadId(threadId)\n\n    suspend fun getAllDMDmLastNotified(): List<DMLastNotified> = dmLastNotifiedDao.getAllDMDmLastNotified()\n\n    suspend fun insertOrUpdateDMLastNotified(\n        threadId: String?,\n        lastNotifiedMsgTs: LocalDateTime?,\n        lastNotifiedAt: LocalDateTime?,\n    ) {\n        if (threadId == null) return\n        val dmLastNotified = getDMLastNotified(threadId)\n        val toUpdate = DMLastNotified(\n            dmLastNotified?.id ?: 0,\n            threadId,\n            lastNotifiedMsgTs,\n            lastNotifiedAt\n        )\n        if (dmLastNotified != null) {\n            dmLastNotifiedDao.updateDMLastNotified(toUpdate)\n            return\n        }\n        dmLastNotifiedDao.insertDMLastNotified(toUpdate)\n    }\n\n    suspend fun deleteDMLastNotified(dmLastNotified: DMLastNotified) = dmLastNotifiedDao.deleteDMLastNotified(dmLastNotified)\n\n    suspend fun deleteAllDMLastNotified() = dmLastNotifiedDao.deleteAllDMLastNotified()\n\n    companion object {\n        private lateinit var INSTANCE: DMLastNotifiedDataSource\n\n        @JvmStatic\n        fun getInstance(context: Context): DMLastNotifiedDataSource {\n            if (!this::INSTANCE.isInitialized) {\n                synchronized(DMLastNotifiedDataSource::class.java) {\n                    if (!this::INSTANCE.isInitialized) {\n                        val database = AppDatabase.getDatabase(context)\n                        INSTANCE = DMLastNotifiedDataSource(database.dmLastNotifiedDao())\n                    }\n                }\n            }\n            return INSTANCE\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/datasources/FavoriteDataSource.kt",
    "content": "package awais.instagrabber.db.datasources\n\nimport android.content.Context\nimport awais.instagrabber.db.AppDatabase\nimport awais.instagrabber.db.dao.FavoriteDao\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.models.enums.FavoriteType\n\nclass FavoriteDataSource(private val favoriteDao: FavoriteDao) {\n    suspend fun getFavorite(query: String, type: FavoriteType): Favorite? = favoriteDao.findFavoriteByQueryAndType(query, type)\n\n    suspend fun getAllFavorites(): List<Favorite> = favoriteDao.getAllFavorites()\n\n    suspend fun insertOrUpdateFavorite(favorite: Favorite) {\n        if (favorite.id != 0) {\n            favoriteDao.updateFavorites(favorite)\n            return\n        }\n        favoriteDao.insertFavorites(favorite)\n    }\n\n    suspend fun deleteFavorite(query: String?, type: FavoriteType?) {\n        if (query == null || type == null) return\n        val favorite = getFavorite(query, type) ?: return\n        favoriteDao.deleteFavorites(favorite)\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: FavoriteDataSource? = null\n\n        fun getInstance(context: Context): FavoriteDataSource {\n            return INSTANCE ?: synchronized(this) {\n                val dao: FavoriteDao = AppDatabase.getDatabase(context).favoriteDao()\n                FavoriteDataSource(dao).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/datasources/RecentSearchDataSource.kt",
    "content": "package awais.instagrabber.db.datasources\n\nimport android.content.Context\nimport awais.instagrabber.db.AppDatabase\nimport awais.instagrabber.db.dao.RecentSearchDao\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.models.enums.FavoriteType\n\nclass RecentSearchDataSource private constructor(private val recentSearchDao: RecentSearchDao) {\n\n    suspend fun getRecentSearchByIgIdAndType(igId: String, type: FavoriteType): RecentSearch? =\n        recentSearchDao.getRecentSearchByIgIdAndType(igId, type)\n\n    suspend fun getAllRecentSearches(): List<RecentSearch> = recentSearchDao.getAllRecentSearches()\n\n    suspend fun insertOrUpdateRecentSearch(recentSearch: RecentSearch) {\n        if (recentSearch.id != 0) {\n            recentSearchDao.updateRecentSearch(recentSearch)\n            return\n        }\n        recentSearchDao.insertRecentSearch(recentSearch)\n    }\n\n    suspend fun deleteRecentSearch(recentSearch: RecentSearch) = recentSearchDao.deleteRecentSearch(recentSearch)\n\n    companion object {\n        private lateinit var INSTANCE: RecentSearchDataSource\n\n        @JvmStatic\n        @Synchronized\n        fun getInstance(context: Context): RecentSearchDataSource {\n            if (!this::INSTANCE.isInitialized) {\n                synchronized(RecentSearchDataSource::class.java) {\n                    if (!this::INSTANCE.isInitialized) {\n                        val database = AppDatabase.getDatabase(context)\n                        INSTANCE = RecentSearchDataSource(database.recentSearchDao())\n                    }\n                }\n            }\n            return INSTANCE\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/entities/Account.kt",
    "content": "package awais.instagrabber.db.entities\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.Ignore\nimport androidx.room.PrimaryKey\n\n@Entity(tableName = Account.TABLE_NAME)\ndata class Account(\n    @ColumnInfo(name = COL_ID) @PrimaryKey(autoGenerate = true) val id: Int,\n    @ColumnInfo(name = COL_UID) val uid: String?,\n    @ColumnInfo(name = COL_USERNAME) val username: String?,\n    @ColumnInfo(name = COL_COOKIE) val cookie: String?,\n    @ColumnInfo(name = COL_FULL_NAME) val fullName: String?,\n    @ColumnInfo(name = COL_PROFILE_PIC) val profilePic: String?,\n) {\n    @Ignore\n    var isSelected = false\n\n    val isValid: Boolean\n        get() = !uid.isNullOrBlank() && !username.isNullOrBlank() && !cookie.isNullOrBlank()\n\n    companion object {\n        const val TABLE_NAME = \"accounts\"\n        const val COL_ID = \"id\"\n        const val COL_USERNAME = \"username\"\n        const val COL_COOKIE = \"cookie\"\n        const val COL_UID = \"uid\"\n        const val COL_FULL_NAME = \"full_name\"\n        const val COL_PROFILE_PIC = \"profile_pic\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/entities/DMLastNotified.kt",
    "content": "package awais.instagrabber.db.entities\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.Index\nimport androidx.room.PrimaryKey\nimport java.time.LocalDateTime\n\n@Entity(tableName = DMLastNotified.TABLE_NAME, indices = [Index(value = [DMLastNotified.COL_THREAD_ID], unique = true)])\ndata class DMLastNotified(\n    @ColumnInfo(name = COL_ID) @PrimaryKey(autoGenerate = true) val id: Int,\n    @ColumnInfo(name = COL_THREAD_ID) val threadId: String?,\n    @ColumnInfo(name = COL_LAST_NOTIFIED_MSG_TS) val lastNotifiedMsgTs: LocalDateTime?,\n    @ColumnInfo(name = COL_LAST_NOTIFIED_AT) val lastNotifiedAt: LocalDateTime?,\n) {\n    companion object {\n        const val TABLE_NAME = \"dm_last_notified\"\n        const val COL_ID = \"id\"\n        const val COL_THREAD_ID = \"thread_id\"\n        const val COL_LAST_NOTIFIED_MSG_TS = \"last_notified_msg_ts\"\n        const val COL_LAST_NOTIFIED_AT = \"last_notified_at\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/entities/Favorite.kt",
    "content": "package awais.instagrabber.db.entities\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport awais.instagrabber.models.enums.FavoriteType\nimport java.time.LocalDateTime\n\n@Entity(tableName = Favorite.TABLE_NAME)\ndata class Favorite(\n    @ColumnInfo(name = COL_ID) @PrimaryKey(autoGenerate = true) val id: Int,\n    @ColumnInfo(name = COL_QUERY) val query: String?,\n    @ColumnInfo(name = COL_TYPE) val type: FavoriteType?,\n    @ColumnInfo(name = COL_DISPLAY_NAME) val displayName: String?,\n    @ColumnInfo(name = COL_PIC_URL) val picUrl: String?,\n    @ColumnInfo(name = COL_DATE_ADDED) val dateAdded: LocalDateTime?,\n) {\n    companion object {\n        const val TABLE_NAME = \"favorites\"\n        const val COL_ID = \"id\"\n        const val COL_QUERY = \"query_text\"\n        const val COL_TYPE = \"type\"\n        const val COL_DISPLAY_NAME = \"display_name\"\n        const val COL_PIC_URL = \"pic_url\"\n        const val COL_DATE_ADDED = \"date_added\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/entities/RecentSearch.kt",
    "content": "package awais.instagrabber.db.entities\n\nimport android.util.Log\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.Index\nimport androidx.room.PrimaryKey\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.repositories.responses.search.SearchItem\nimport awais.instagrabber.utils.extensions.TAG\nimport java.time.LocalDateTime\n\n@Entity(tableName = RecentSearch.TABLE_NAME, indices = [Index(value = [RecentSearch.COL_IG_ID, RecentSearch.COL_TYPE], unique = true)])\ndata class RecentSearch(\n    @ColumnInfo(name = COL_ID) @PrimaryKey(autoGenerate = true) val id: Int,\n    @ColumnInfo(name = COL_IG_ID) val igId: String,\n    @ColumnInfo(name = COL_NAME) val name: String,\n    @ColumnInfo(name = COL_USERNAME) val username: String?,\n    @ColumnInfo(name = COL_PIC_URL) val picUrl: String?,\n    @ColumnInfo(name = COL_TYPE) val type: FavoriteType,\n    @ColumnInfo(name = COL_LAST_SEARCHED_ON) val lastSearchedOn: LocalDateTime,\n) {\n\n    companion object {\n        const val TABLE_NAME = \"recent_searches\"\n        private const val COL_ID = \"id\"\n        const val COL_IG_ID = \"ig_id\"\n        private const val COL_NAME = \"name\"\n        private const val COL_USERNAME = \"username\"\n        private const val COL_PIC_URL = \"pic_url\"\n        const val COL_TYPE = \"type\"\n        private const val COL_LAST_SEARCHED_ON = \"last_searched_on\"\n\n        @JvmStatic\n        fun fromSearchItem(searchItem: SearchItem): RecentSearch? {\n            val type = searchItem.type ?: return null\n            try {\n                val igId: String\n                val name: String\n                val username: String?\n                val picUrl: String?\n                when (type) {\n                    FavoriteType.USER -> {\n                        igId = searchItem.user.pk.toString()\n                        name = searchItem.user.fullName ?: \"\"\n                        username = searchItem.user.username\n                        picUrl = searchItem.user.profilePicUrl\n                    }\n                    FavoriteType.HASHTAG -> {\n                        igId = searchItem.hashtag.id\n                        name = searchItem.hashtag.name\n                        username = null\n                        picUrl = null\n                    }\n                    FavoriteType.LOCATION -> {\n                        igId = searchItem.place.location.pk.toString()\n                        name = searchItem.place.title\n                        username = null\n                        picUrl = null\n                    }\n                    else -> return null\n                }\n                return RecentSearch(id = 0, igId, name, username, picUrl, type, LocalDateTime.now())\n            } catch (e: Exception) {\n                Log.e(TAG, \"fromSearchItem: \", e)\n            }\n            return null\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/repositories/AccountRepository.kt",
    "content": "package awais.instagrabber.db.repositories\n\nimport android.content.Context\nimport awais.instagrabber.db.datasources.AccountDataSource\nimport awais.instagrabber.db.entities.Account\n\nclass AccountRepository(private val accountDataSource: AccountDataSource) {\n    suspend fun getAccount(uid: Long): Account? = accountDataSource.getAccount(uid.toString())\n\n    suspend fun getAllAccounts(): List<Account> = accountDataSource.getAllAccounts()\n\n    suspend fun insertOrUpdateAccounts(accounts: List<Account>) {\n        for (account in accounts) {\n            accountDataSource.insertOrUpdateAccount(\n                account.uid,\n                account.username,\n                account.cookie,\n                account.fullName,\n                account.profilePic\n            )\n        }\n    }\n\n    suspend fun insertOrUpdateAccount(\n        uid: Long,\n        username: String,\n        cookie: String,\n        fullName: String,\n        profilePicUrl: String?,\n    ): Account? {\n        accountDataSource.insertOrUpdateAccount(uid.toString(), username, cookie, fullName, profilePicUrl)\n        return accountDataSource.getAccount(uid.toString())\n    }\n\n    suspend fun deleteAccount(account: Account) = accountDataSource.deleteAccount(account)\n\n    suspend fun deleteAllAccounts() = accountDataSource.deleteAllAccounts()\n\n    companion object {\n        @Volatile\n        private var INSTANCE: AccountRepository? = null\n\n        fun getInstance(context: Context): AccountRepository {\n            return INSTANCE ?: synchronized(this) {\n                val dataSource: AccountDataSource = AccountDataSource.getInstance(context)\n                AccountRepository(dataSource).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/repositories/DMLastNotifiedRepository.kt",
    "content": "package awais.instagrabber.db.repositories\n\nimport awais.instagrabber.db.datasources.DMLastNotifiedDataSource\nimport awais.instagrabber.db.entities.DMLastNotified\nimport java.time.LocalDateTime\n\nclass DMLastNotifiedRepository private constructor(private val dmLastNotifiedDataSource: DMLastNotifiedDataSource) {\n\n    suspend fun getDMLastNotified(threadId: String): DMLastNotified? = dmLastNotifiedDataSource.getDMLastNotified(threadId)\n\n    suspend fun getAllDMDmLastNotified(): List<DMLastNotified> = dmLastNotifiedDataSource.getAllDMDmLastNotified()\n\n    suspend fun insertOrUpdateDMLastNotified(dmLastNotifiedList: List<DMLastNotified>) {\n        for (dmLastNotified in dmLastNotifiedList) {\n            dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(\n                dmLastNotified.threadId,\n                dmLastNotified.lastNotifiedMsgTs,\n                dmLastNotified.lastNotifiedAt\n            )\n        }\n    }\n\n    suspend fun insertOrUpdateDMLastNotified(\n        threadId: String,\n        lastNotifiedMsgTs: LocalDateTime,\n        lastNotifiedAt: LocalDateTime,\n    ): DMLastNotified? {\n        dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(threadId, lastNotifiedMsgTs, lastNotifiedAt)\n        return dmLastNotifiedDataSource.getDMLastNotified(threadId)\n    }\n\n    suspend fun deleteDMLastNotified(dmLastNotified: DMLastNotified) = dmLastNotifiedDataSource.deleteDMLastNotified(dmLastNotified)\n\n    suspend fun deleteAllDMLastNotified() = dmLastNotifiedDataSource.deleteAllDMLastNotified()\n\n    companion object {\n        private lateinit var instance: DMLastNotifiedRepository\n\n        @JvmStatic\n        fun getInstance(dmLastNotifiedDataSource: DMLastNotifiedDataSource): DMLastNotifiedRepository {\n            if (!this::instance.isInitialized) {\n                instance = DMLastNotifiedRepository(dmLastNotifiedDataSource)\n            }\n            return instance\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/repositories/FavoriteRepository.kt",
    "content": "package awais.instagrabber.db.repositories\n\nimport android.content.Context\nimport awais.instagrabber.db.datasources.FavoriteDataSource\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.models.enums.FavoriteType\n\nclass FavoriteRepository(private val favoriteDataSource: FavoriteDataSource) {\n\n    suspend fun getFavorite(query: String, type: FavoriteType): Favorite? = favoriteDataSource.getFavorite(query, type)\n\n    suspend fun getAllFavorites(): List<Favorite> = favoriteDataSource.getAllFavorites()\n\n    suspend fun insertOrUpdateFavorite(favorite: Favorite) = favoriteDataSource.insertOrUpdateFavorite(favorite)\n\n    suspend fun deleteFavorite(query: String?, type: FavoriteType?) = favoriteDataSource.deleteFavorite(query, type)\n\n    companion object {\n        @Volatile\n        private var INSTANCE: FavoriteRepository? = null\n\n        fun getInstance(context: Context): FavoriteRepository {\n            return INSTANCE ?: synchronized(this) {\n                val dataSource: FavoriteDataSource = FavoriteDataSource.getInstance(context)\n                FavoriteRepository(dataSource).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/db/repositories/RecentSearchRepository.kt",
    "content": "package awais.instagrabber.db.repositories\n\nimport awais.instagrabber.db.datasources.RecentSearchDataSource\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.models.enums.FavoriteType\nimport java.time.LocalDateTime\n\nclass RecentSearchRepository private constructor(private val recentSearchDataSource: RecentSearchDataSource) {\n    suspend fun getRecentSearch(igId: String, type: FavoriteType): RecentSearch? = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type)\n\n    suspend fun getAllRecentSearches(): List<RecentSearch> = recentSearchDataSource.getAllRecentSearches()\n\n    suspend fun insertOrUpdateRecentSearch(recentSearch: RecentSearch) =\n        insertOrUpdateRecentSearch(recentSearch.igId, recentSearch.name, recentSearch.username, recentSearch.picUrl, recentSearch.type)\n\n    private suspend fun insertOrUpdateRecentSearch(\n        igId: String,\n        name: String,\n        username: String?,\n        picUrl: String?,\n        type: FavoriteType,\n    ) {\n        var recentSearch = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type)\n        recentSearch = RecentSearch(recentSearch?.id ?: 0, igId, name, username, picUrl, type, LocalDateTime.now())\n        recentSearchDataSource.insertOrUpdateRecentSearch(recentSearch)\n    }\n\n    suspend fun deleteRecentSearchByIgIdAndType(igId: String, type: FavoriteType) {\n        val recentSearch = recentSearchDataSource.getRecentSearchByIgIdAndType(igId, type)\n        if (recentSearch != null) {\n            recentSearchDataSource.deleteRecentSearch(recentSearch)\n        }\n    }\n\n    suspend fun deleteRecentSearch(recentSearch: RecentSearch) = recentSearchDataSource.deleteRecentSearch(recentSearch)\n\n    companion object {\n        private lateinit var instance: RecentSearchRepository\n\n        @JvmStatic\n        fun getInstance(recentSearchDataSource: RecentSearchDataSource): RecentSearchRepository {\n            if (!this::instance.isInitialized) {\n                instance = RecentSearchRepository(recentSearchDataSource)\n            }\n            return instance\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/AccountSwitcherDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.AccountSwitcherAdapter;\nimport awais.instagrabber.databinding.DialogAccountSwitcherBinding;\nimport awais.instagrabber.db.entities.Account;\nimport awais.instagrabber.db.repositories.AccountRepository;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.ProcessPhoenix;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class AccountSwitcherDialogFragment extends DialogFragment {\n    private static final String TAG = AccountSwitcherDialogFragment.class.getSimpleName();\n\n    private AccountRepository accountRepository;\n\n    private OnAddAccountClickListener onAddAccountClickListener;\n    private DialogAccountSwitcherBinding binding;\n\n    public AccountSwitcherDialogFragment() {}\n\n    public AccountSwitcherDialogFragment(final OnAddAccountClickListener onAddAccountClickListener) {\n        this.onAddAccountClickListener = onAddAccountClickListener;\n    }\n\n    private final AccountSwitcherAdapter.OnAccountClickListener accountClickListener = (model, isCurrent) -> {\n        if (isCurrent) {\n            dismiss();\n            return;\n        }\n        CookieUtils.setupCookies(model.getCookie());\n        settingsHelper.putString(Constants.COOKIE, model.getCookie());\n        // final FragmentActivity activity = getActivity();\n        // if (activity != null) activity.recreate();\n        // dismiss();\n        AppExecutors.INSTANCE.getMainThread().execute(() -> {\n            final Context context = getContext();\n            if (context == null) return;\n            ProcessPhoenix.triggerRebirth(context);\n        }, 200);\n    };\n\n    private final AccountSwitcherAdapter.OnAccountLongClickListener accountLongClickListener = (model, isCurrent) -> {\n        final Context context = getContext();\n        if (context == null) return false;\n        if (isCurrent) {\n            new AlertDialog.Builder(context)\n                    .setMessage(R.string.quick_access_cannot_delete_curr)\n                    .setPositiveButton(R.string.ok, null)\n                    .show();\n            return true;\n        }\n        new AlertDialog.Builder(context)\n                .setMessage(getString(R.string.quick_access_confirm_delete, model.getUsername()))\n                .setPositiveButton(R.string.yes, (dialog, which) -> {\n                    if (accountRepository == null) return;\n                    accountRepository.deleteAccount(\n                            model,\n                            CoroutineUtilsKt.getContinuation((unit, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                dismiss();\n                                if (throwable != null) {\n                                    Log.e(TAG, \"deleteAccount: \", throwable);\n                                }\n                            }), Dispatchers.getIO())\n                    );\n                })\n                .setNegativeButton(R.string.cancel, null)\n                .show();\n        dismiss();\n        return true;\n    };\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        binding = DialogAccountSwitcherBinding.inflate(inflater, container, false);\n        binding.accounts.setLayoutManager(new LinearLayoutManager(getContext()));\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        init();\n    }\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        accountRepository = AccountRepository.Companion.getInstance(context);\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        final int height = ViewGroup.LayoutParams.WRAP_CONTENT;\n        final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);\n        window.setLayout(width, height);\n    }\n\n    private void init() {\n        final AccountSwitcherAdapter adapter = new AccountSwitcherAdapter(accountClickListener, accountLongClickListener);\n        binding.accounts.setAdapter(adapter);\n        if (accountRepository == null) return;\n        accountRepository.getAllAccounts(\n                CoroutineUtilsKt.getContinuation((accounts, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"init: \", throwable);\n                        return;\n                    }\n                    if (accounts == null) return;\n                    final String cookie = settingsHelper.getString(Constants.COOKIE);\n                    final List<Account> copy = new ArrayList<>(accounts);\n                    sortUserList(cookie, copy);\n                    adapter.submitList(copy);\n                }), Dispatchers.getIO())\n        );\n        binding.addAccountBtn.setOnClickListener(v -> {\n            if (onAddAccountClickListener == null) return;\n            onAddAccountClickListener.onAddAccountClick(this);\n        });\n    }\n\n    /**\n     * Sort the user list by following logic:\n     * <ol>\n     * <li>Keep currently active account at top.\n     * <li>Check if any user does not have a full name.\n     * <li>If all have full names, sort by full names.\n     * <li>Otherwise, sort by the usernames\n     * </ol>\n     *\n     * @param cookie   active cookie\n     * @param allUsers list of users\n     */\n    private void sortUserList(final String cookie, final List<Account> allUsers) {\n        boolean sortByName = true;\n        for (final Account user : allUsers) {\n            if (TextUtils.isEmpty(user.getFullName())) {\n                sortByName = false;\n                break;\n            }\n        }\n        final boolean finalSortByName = sortByName;\n        Collections.sort(allUsers, (o1, o2) -> {\n            // keep current account at top\n            if (o1.getCookie().equals(cookie)) return -1;\n            if (finalSortByName) {\n                // sort by full name\n                return o1.getFullName().compareTo(o2.getFullName());\n            }\n            // otherwise sort by username\n            return o1.getUsername().compareTo(o2.getUsername());\n        });\n    }\n\n    public interface OnAddAccountClickListener {\n        void onAddAccountClick(final AccountSwitcherDialogFragment dialogFragment);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.method.LinkMovementMethod;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringRes;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\n\nimport awais.instagrabber.R;\n\npublic class ConfirmDialogFragment extends DialogFragment {\n    private Context context;\n    private ConfirmDialogFragmentCallback callback;\n\n    private final int defaultPositiveButtonText = R.string.ok;\n    // private final int defaultNegativeButtonText = R.string.cancel;\n\n    @NonNull\n    public static ConfirmDialogFragment newInstance(final int requestCode,\n                                                    @StringRes final int title,\n                                                    @NonNull final CharSequence message,\n                                                    @StringRes final int positiveText,\n                                                    @StringRes final int negativeText,\n                                                    @StringRes final int neutralText) {\n        return newInstance(requestCode, title, 0, message, positiveText, negativeText, neutralText);\n    }\n\n    @NonNull\n    public static ConfirmDialogFragment newInstance(final int requestCode,\n                                                    @StringRes final int title,\n                                                    @StringRes final int messageResId,\n                                                    @StringRes final int positiveText,\n                                                    @StringRes final int negativeText,\n                                                    @StringRes final int neutralText) {\n        return newInstance(requestCode, title, messageResId, null, positiveText, negativeText, neutralText);\n    }\n\n    @NonNull\n    private static ConfirmDialogFragment newInstance(final int requestCode,\n                                                     @StringRes final int title,\n                                                     @StringRes final int messageResId,\n                                                     @Nullable final CharSequence message,\n                                                     @StringRes final int positiveText,\n                                                     @StringRes final int negativeText,\n                                                     @StringRes final int neutralText) {\n        Bundle args = new Bundle();\n        args.putInt(\"requestCode\", requestCode);\n        if (title != 0) {\n            args.putInt(\"title\", title);\n        }\n        if (messageResId != 0) {\n            args.putInt(\"messageResId\", messageResId);\n        } else if (message != null) {\n            args.putCharSequence(\"message\", message);\n        }\n        if (positiveText != 0) {\n            args.putInt(\"positive\", positiveText);\n        }\n        if (negativeText != 0) {\n            args.putInt(\"negative\", negativeText);\n        }\n        if (neutralText != 0) {\n            args.putInt(\"neutral\", neutralText);\n        }\n        ConfirmDialogFragment fragment = new ConfirmDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n\n    }\n\n    public ConfirmDialogFragment() {}\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        this.context = context;\n        final Fragment parentFragment = getParentFragment();\n        if (parentFragment instanceof ConfirmDialogFragmentCallback) {\n            callback = (ConfirmDialogFragmentCallback) parentFragment;\n            return;\n        }\n        final FragmentActivity fragmentActivity = getActivity();\n        if (fragmentActivity instanceof ConfirmDialogFragmentCallback) {\n            callback = (ConfirmDialogFragmentCallback) fragmentActivity;\n        }\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {\n        final Bundle arguments = getArguments();\n        int title = 0;\n        int messageResId = 0;\n        CharSequence message = null;\n        int neutralButtonText = 0;\n        int negativeButtonText = 0;\n\n        final int positiveButtonText;\n        final int requestCode;\n        if (arguments != null) {\n            title = arguments.getInt(\"title\", 0);\n            messageResId = arguments.getInt(\"messageResId\", 0);\n            message = arguments.getCharSequence(\"message\", null);\n            positiveButtonText = arguments.getInt(\"positive\", defaultPositiveButtonText);\n            negativeButtonText = arguments.getInt(\"negative\", 0);\n            neutralButtonText = arguments.getInt(\"neutral\", 0);\n            requestCode = arguments.getInt(\"requestCode\", 0);\n        } else {\n            requestCode = 0;\n            positiveButtonText = defaultPositiveButtonText;\n        }\n        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)\n                .setPositiveButton(positiveButtonText, (d, w) -> {\n                    if (callback == null) return;\n                    callback.onPositiveButtonClicked(requestCode);\n                });\n        if (title != 0) {\n            builder.setTitle(title);\n        }\n        if (messageResId != 0) {\n            builder.setMessage(messageResId);\n        } else if (message != null) {\n            builder.setMessage(message);\n        }\n        if (negativeButtonText != 0) {\n            builder.setNegativeButton(negativeButtonText, (dialog, which) -> {\n                if (callback == null) return;\n                callback.onNegativeButtonClicked(requestCode);\n            });\n        }\n        if (neutralButtonText != 0) {\n            builder.setNeutralButton(neutralButtonText, (dialog, which) -> {\n                if (callback == null) return;\n                callback.onNeutralButtonClicked(requestCode);\n            });\n        }\n        return builder.create();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final TextView view = dialog.findViewById(android.R.id.message);\n        view.setMovementMethod(LinkMovementMethod.getInstance());\n    }\n\n    public interface ConfirmDialogFragmentCallback {\n        void onPositiveButtonClicked(int requestCode);\n\n        void onNegativeButtonClicked(int requestCode);\n\n        void onNeutralButtonClicked(int requestCode);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/CreateBackupDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.provider.DocumentsContract;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.inputmethod.InputMethodManager;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\n\nimport java.time.format.DateTimeFormatter;\nimport java.time.LocalDateTime;\nimport java.util.Locale;\n\nimport awais.instagrabber.databinding.DialogCreateBackupBinding;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.ExportImportUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static android.app.Activity.RESULT_OK;\n\npublic class CreateBackupDialogFragment extends DialogFragment {\n    private static final String TAG = CreateBackupDialogFragment.class.getSimpleName();\n    private static final int STORAGE_PERM_REQUEST_CODE = 8020;\n    private static final DateTimeFormatter BACKUP_FILE_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern(\"yyyyMMddHHmmss\", Locale.US);\n    private static final int CREATE_FILE_REQUEST_CODE = 1;\n\n\n    private final OnResultListener onResultListener;\n    private DialogCreateBackupBinding binding;\n\n    public CreateBackupDialogFragment(final OnResultListener onResultListener) {\n        this.onResultListener = onResultListener;\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        binding = DialogCreateBackupBinding.inflate(inflater, container, false);\n        return binding.getRoot();\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        Dialog dialog = super.onCreateDialog(savedInstanceState);\n        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);\n        return dialog;\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        final int height = ViewGroup.LayoutParams.WRAP_CONTENT;\n        final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);\n        window.setLayout(width, height);\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        init();\n    }\n\n    private void init() {\n        binding.etPassword.addTextChangedListener(new TextWatcher() {\n            @Override\n            public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}\n\n            @Override\n            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n                binding.btnSaveTo.setEnabled(!TextUtils.isEmpty(s));\n            }\n\n            @Override\n            public void afterTextChanged(final Editable s) {}\n        });\n        final Context context = getContext();\n        if (context == null) {\n            return;\n        }\n        binding.cbPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {\n            if (isChecked) {\n                if (TextUtils.isEmpty(binding.etPassword.getText())) {\n                    binding.btnSaveTo.setEnabled(false);\n                }\n                binding.passwordField.setVisibility(View.VISIBLE);\n                binding.etPassword.requestFocus();\n                final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n                if (imm == null) return;\n                imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT);\n                return;\n            }\n            binding.btnSaveTo.setEnabled(true);\n            binding.passwordField.setVisibility(View.GONE);\n            final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n            if (imm == null) return;\n            imm.hideSoftInputFromWindow(binding.etPassword.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);\n        });\n        binding.btnSaveTo.setOnClickListener(v -> {\n            createFile();\n        });\n    }\n\n    @Override\n    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {\n        if (data == null || data.getData() == null) return;\n        if (resultCode != RESULT_OK || requestCode != CREATE_FILE_REQUEST_CODE) return;\n        final Context context = getContext();\n        if (context == null) return;\n        final Editable passwordText = binding.etPassword.getText();\n        final String password = binding.cbPassword.isChecked()\n                                        && passwordText != null\n                                        && !TextUtils.isEmpty(passwordText.toString())\n                                ? passwordText.toString().trim()\n                                : null;\n        int flags = 0;\n        if (binding.cbExportFavorites.isChecked()) {\n            flags |= ExportImportUtils.FLAG_FAVORITES;\n        }\n        if (binding.cbExportSettings.isChecked()) {\n            flags |= ExportImportUtils.FLAG_SETTINGS;\n        }\n        if (binding.cbExportLogins.isChecked()) {\n            flags |= ExportImportUtils.FLAG_COOKIES;\n        }\n        ExportImportUtils.exportData(context, flags, data.getData(), password, result -> {\n            if (onResultListener != null) {\n                onResultListener.onResult(result);\n            }\n            dismiss();\n        });\n    }\n\n    private void createFile() {\n        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);\n        intent.addCategory(Intent.CATEGORY_OPENABLE);\n        intent.setType(\"application/octet-stream\");\n        final String fileName = String.format(\"barinsta_%s.backup\", LocalDateTime.now().format(BACKUP_FILE_DATE_TIME_FORMAT));\n        intent.putExtra(Intent.EXTRA_TITLE, fileName);\n\n        // Optionally, specify a URI for the directory that should be opened in\n        // the system file picker when your app creates the document.\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, DownloadUtils.getBackupsDir().getUri());\n        }\n\n        startActivityForResult(intent, CREATE_FILE_REQUEST_CODE);\n    }\n\n\n    public interface OnResultListener {\n        void onResult(boolean result);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/DirectItemReactionDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.bottomsheet.BottomSheetDialog;\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectReactionsAdapter;\nimport awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DirectItemReactionDialogFragment extends BottomSheetDialogFragment {\n\n    private static final String ARG_VIEWER_ID = \"viewerId\";\n    private static final String ARG_ITEM_ID = \"itemId\";\n    private static final String ARG_USERS = \"users\";\n    private static final String ARG_REACTIONS = \"reactions\";\n\n    private RecyclerView recyclerView;\n    private OnReactionClickListener onReactionClickListener;\n\n    public static DirectItemReactionDialogFragment newInstance(final long viewerId,\n                                                               @NonNull final ArrayList<User> users,\n                                                               @NonNull final String itemId,\n                                                               @NonNull final DirectItemReactions reactions) {\n        Bundle args = new Bundle();\n        args.putLong(ARG_VIEWER_ID, viewerId);\n        args.putSerializable(ARG_USERS, users);\n        args.putString(ARG_ITEM_ID, itemId);\n        args.putSerializable(ARG_REACTIONS, reactions);\n        DirectItemReactionDialogFragment fragment = new DirectItemReactionDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public DirectItemReactionDialogFragment() {}\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        final Context context = getContext();\n        if (context == null) {\n            return null;\n        }\n        recyclerView = new RecyclerView(context);\n        return recyclerView;\n\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init();\n    }\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        try {\n            onReactionClickListener = (OnReactionClickListener) getParentFragment();\n        } catch (ClassCastException e) {\n            throw new ClassCastException(\"Calling fragment must implement DirectReactionsAdapter.OnReactionClickListener interface\");\n        }\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;\n        final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);\n        if (bottomSheetInternal == null) return;\n        bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;\n        bottomSheetInternal.requestLayout();\n    }\n\n    private void init() {\n        final Context context = getContext();\n        if (context == null) return;\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final long viewerId = arguments.getLong(ARG_VIEWER_ID);\n        final Serializable usersSerializable = arguments.getSerializable(ARG_USERS);\n        if (!(usersSerializable instanceof ArrayList)) return;\n        //noinspection unchecked\n        final List<User> users = (ArrayList<User>) usersSerializable;\n        final Serializable reactionsSerializable = arguments.getSerializable(ARG_REACTIONS);\n        if (!(reactionsSerializable instanceof DirectItemReactions)) return;\n        final DirectItemReactions reactions = (DirectItemReactions) reactionsSerializable;\n        final String itemId = arguments.getString(ARG_ITEM_ID);\n        if (TextUtils.isEmpty(itemId)) return;\n        recyclerView.setLayoutManager(new LinearLayoutManager(context));\n        final DirectReactionsAdapter adapter = new DirectReactionsAdapter(viewerId, users, itemId, onReactionClickListener);\n        recyclerView.setAdapter(adapter);\n        adapter.submitList(reactions.getEmojis());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/EditTextDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.widget.AppCompatEditText;\nimport androidx.fragment.app.DialogFragment;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\npublic class EditTextDialogFragment extends DialogFragment {\n\n    private final int margin;\n    private final int topMargin;\n\n    private Context context;\n    private EditTextDialogFragmentCallback callback;\n\n    public static EditTextDialogFragment newInstance(@StringRes final int title,\n                                                     @StringRes final int positiveText,\n                                                     @StringRes final int negativeText,\n                                                     @Nullable final String initialText) {\n        Bundle args = new Bundle();\n        args.putInt(\"title\", title);\n        args.putInt(\"positive\", positiveText);\n        args.putInt(\"negative\", negativeText);\n        args.putString(\"initial\", initialText);\n        EditTextDialogFragment fragment = new EditTextDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public EditTextDialogFragment() {\n        margin = Utils.convertDpToPx(20);\n        topMargin = Utils.convertDpToPx(8);\n    }\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        try {\n            callback = (EditTextDialogFragmentCallback) getParentFragment();\n        } catch (ClassCastException e) {\n            throw new ClassCastException(\"Calling fragment must implement EditTextDialogFragmentCallback interface\");\n        }\n        this.context = context;\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {\n        final Bundle arguments = getArguments();\n        int title = -1;\n        int positiveButtonText = R.string.ok;\n        int negativeButtonText = R.string.cancel;\n        String initialText = null;\n        if (arguments != null) {\n            title = arguments.getInt(\"title\", -1);\n            positiveButtonText = arguments.getInt(\"positive\", R.string.ok);\n            negativeButtonText = arguments.getInt(\"negative\", R.string.cancel);\n            initialText = arguments.getString(\"initial\", null);\n        }\n        final AppCompatEditText input = new AppCompatEditText(context);\n        if (!TextUtils.isEmpty(initialText)) {\n            input.setText(initialText);\n        }\n        final FrameLayout container = new FrameLayout(context);\n        final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                                                                                   ViewGroup.LayoutParams.WRAP_CONTENT);\n        layoutParams.leftMargin = margin;\n        layoutParams.rightMargin = margin;\n        layoutParams.topMargin = topMargin;\n        input.setLayoutParams(layoutParams);\n        container.addView(input);\n        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)\n                .setView(container)\n                .setPositiveButton(positiveButtonText, (d, w) -> {\n                    final String string = input.getText() != null ? input.getText().toString() : \"\";\n                    if (callback != null) {\n                        callback.onPositiveButtonClicked(string);\n                    }\n                })\n                .setNegativeButton(negativeButtonText, (dialog, which) -> {\n                    if (callback != null) {\n                        callback.onNegativeButtonClicked();\n                    }\n                });\n        if (title > 0) {\n            builder.setTitle(title);\n        }\n        return builder.create();\n    }\n\n    public interface EditTextDialogFragmentCallback {\n        void onPositiveButtonClicked(String text);\n\n        void onNegativeButtonClicked();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/GifPickerBottomDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.recyclerview.widget.GridLayoutManager;\n\nimport com.google.android.material.bottomsheet.BottomSheetDialog;\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment;\nimport com.google.android.material.snackbar.Snackbar;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.GifItemsAdapter;\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter;\nimport awais.instagrabber.databinding.LayoutGifPickerBinding;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif;\nimport awais.instagrabber.utils.Debouncer;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.viewmodels.GifPickerViewModel;\n\npublic class GifPickerBottomDialogFragment extends BottomSheetDialogFragment {\n    private static final String TAG = GifPickerBottomDialogFragment.class.getSimpleName();\n    private static final int INPUT_DEBOUNCE_INTERVAL = 500;\n    private static final String INPUT_KEY = \"gif_search_input\";\n\n    private LayoutGifPickerBinding binding;\n    private GifPickerViewModel viewModel;\n    private GifItemsAdapter gifItemsAdapter;\n    private OnSelectListener onSelectListener;\n    private Debouncer<String> inputDebouncer;\n\n    public static GifPickerBottomDialogFragment newInstance() {\n        final Bundle args = new Bundle();\n        final GifPickerBottomDialogFragment fragment = new GifPickerBottomDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);\n        final Debouncer.Callback<String> callback = new Debouncer.Callback<String>() {\n            @Override\n            public void call(final String key) {\n                final Editable text = binding.input.getText();\n                if (TextUtils.isEmpty(text)) {\n                    viewModel.search(null);\n                    return;\n                }\n                viewModel.search(text.toString().trim());\n            }\n\n            @Override\n            public void onError(final Throwable t) {\n                Log.e(TAG, \"onError: \", t);\n            }\n        };\n        inputDebouncer = new Debouncer<>(callback, INPUT_DEBOUNCE_INTERVAL);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = LayoutGifPickerBinding.inflate(inflater, container, false);\n        viewModel = new ViewModelProvider(this).get(GifPickerViewModel.class);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;\n        final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);\n        if (bottomSheetInternal == null) return;\n        bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;\n        bottomSheetInternal.requestLayout();\n    }\n\n    private void init() {\n        setupList();\n        setupInput();\n        setupObservers();\n    }\n\n    private void setupList() {\n        final Context context = getContext();\n        if (context == null) return;\n        binding.gifList.setLayoutManager(new GridLayoutManager(context, 3));\n        binding.gifList.setHasFixedSize(true);\n        gifItemsAdapter = new GifItemsAdapter(entry -> {\n            if (onSelectListener == null) return;\n            onSelectListener.onSelect(entry);\n        });\n        binding.gifList.setAdapter(gifItemsAdapter);\n    }\n\n    private void setupInput() {\n        binding.input.addTextChangedListener(new TextWatcherAdapter() {\n            @Override\n            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n                inputDebouncer.call(INPUT_KEY);\n            }\n        });\n    }\n\n    private void setupObservers() {\n        viewModel.getImages().observe(getViewLifecycleOwner(), imagesResource -> {\n            if (imagesResource == null) return;\n            switch (imagesResource.status) {\n                case SUCCESS:\n                    gifItemsAdapter.submitList(imagesResource.data);\n                    break;\n                case ERROR:\n                    final Context context = getContext();\n                    if (context != null && imagesResource.message != null) {\n                        Snackbar.make(context, binding.getRoot(), imagesResource.message, Snackbar.LENGTH_LONG).show();\n                    }\n                    if (context != null && imagesResource.resId != 0) {\n                        Snackbar.make(context, binding.getRoot(), getString(imagesResource.resId), Snackbar.LENGTH_LONG).show();\n                    }\n                    break;\n                case LOADING:\n                    break;\n            }\n        });\n    }\n\n    public void setOnSelectListener(final OnSelectListener onSelectListener) {\n        this.onSelectListener = onSelectListener;\n    }\n\n    public interface OnSelectListener {\n        void onSelect(GiphyGif giphyGif);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/KeywordsFilterDialog.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.KeywordsFilterAdapter;\nimport awais.instagrabber.databinding.DialogKeywordsFilterBinding;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.utils.SettingsHelper;\nimport awais.instagrabber.utils.Utils;\n\npublic final class KeywordsFilterDialog extends DialogFragment {\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        final int height = ViewGroup.LayoutParams.WRAP_CONTENT;\n        final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);\n        window.setLayout(width, height);\n    }\n\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        final DialogKeywordsFilterBinding dialogKeywordsFilterBinding = DialogKeywordsFilterBinding.inflate(inflater, container, false);\n        init(dialogKeywordsFilterBinding, getContext());\n        dialogKeywordsFilterBinding.btnOK.setOnClickListener(view -> this.dismiss());\n        return dialogKeywordsFilterBinding.getRoot();\n    }\n\n    private void init(DialogKeywordsFilterBinding dialogKeywordsFilterBinding, Context context){\n        final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);\n        final RecyclerView recyclerView = dialogKeywordsFilterBinding.recyclerKeyword;\n        recyclerView.setLayoutManager(linearLayoutManager);\n\n        final SettingsHelper settingsHelper = new SettingsHelper(context);\n        final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(PreferenceKeys.KEYWORD_FILTERS));\n        final KeywordsFilterAdapter adapter = new KeywordsFilterAdapter(context, items);\n        recyclerView.setAdapter(adapter);\n\n        final EditText editText = dialogKeywordsFilterBinding.editText;\n\n        dialogKeywordsFilterBinding.btnAdd.setOnClickListener(view ->{\n            final String s = editText.getText().toString();\n            if(s.isEmpty()) return;\n            if(items.contains(s)) {\n                editText.setText(\"\");\n                return;\n            }\n            items.add(s.toLowerCase());\n            settingsHelper.putStringSet(PreferenceKeys.KEYWORD_FILTERS, new HashSet<>(items));\n            adapter.notifyItemInserted(items.size());\n            final String message = context.getString(R.string.added_keywords, s);\n            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();\n            editText.setText(\"\");\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/MultiOptionDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.util.SparseBooleanArray;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.google.common.primitives.Booleans;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class MultiOptionDialogFragment<T extends Serializable> extends DialogFragment {\n    private static final String TAG = MultiOptionDialogFragment.class.getSimpleName();\n\n    public enum Type {\n        MULTIPLE,\n        SINGLE_CHECKED,\n        SINGLE\n    }\n\n    private Context context;\n    private Type type;\n    private MultiOptionDialogCallback<T> callback;\n    private MultiOptionDialogSingleCallback<T> singleCallback;\n    private List<Option<?>> options;\n\n    @NonNull\n    public static <E extends Serializable> MultiOptionDialogFragment<E> newInstance(final int requestCode,\n                                                                                    @StringRes final int title,\n                                                                                    @NonNull final ArrayList<Option<E>> options) {\n        return newInstance(requestCode, title, 0, 0, options, Type.SINGLE);\n    }\n\n    @NonNull\n    public static <E extends Serializable> MultiOptionDialogFragment<E> newInstance(final int requestCode,\n                                                                                    @StringRes final int title,\n                                                                                    @StringRes final int positiveButtonText,\n                                                                                    @StringRes final int negativeButtonText,\n                                                                                    @NonNull final ArrayList<Option<E>> options,\n                                                                                    @NonNull final Type type) {\n        Bundle args = new Bundle();\n        args.putInt(\"requestCode\", requestCode);\n        args.putInt(\"title\", title);\n        args.putInt(\"positiveButtonText\", positiveButtonText);\n        args.putInt(\"negativeButtonText\", negativeButtonText);\n        args.putSerializable(\"options\", options);\n        args.putSerializable(\"type\", type);\n        MultiOptionDialogFragment<E> fragment = new MultiOptionDialogFragment<>();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        this.context = context;\n        final Fragment parentFragment = getParentFragment();\n        if (parentFragment != null) {\n            if (parentFragment instanceof MultiOptionDialogCallback) {\n                callback = (MultiOptionDialogCallback) parentFragment;\n            }\n            if (parentFragment instanceof MultiOptionDialogSingleCallback) {\n                singleCallback = (MultiOptionDialogSingleCallback) parentFragment;\n            }\n            return;\n        }\n        final FragmentActivity fragmentActivity = getActivity();\n        if (fragmentActivity instanceof MultiOptionDialogCallback) {\n            callback = (MultiOptionDialogCallback) fragmentActivity;\n        }\n        if (fragmentActivity instanceof MultiOptionDialogSingleCallback) {\n            singleCallback = (MultiOptionDialogSingleCallback) fragmentActivity;\n        }\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        final Bundle arguments = getArguments();\n        int title = 0;\n        int rc = 0;\n        if (arguments != null) {\n            rc = arguments.getInt(\"requestCode\");\n            title = arguments.getInt(\"title\");\n            type = (Type) arguments.getSerializable(\"type\");\n        }\n        final int requestCode = rc;\n        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);\n        if (title != 0) {\n            builder.setTitle(title);\n        }\n        try {\n            //noinspection unchecked\n            options = arguments != null ? (List<Option<?>>) arguments.getSerializable(\"options\")\n                                        : Collections.emptyList();\n        } catch (Exception e) {\n            Log.e(TAG, \"onCreateDialog: \", e);\n            options = Collections.emptyList();\n        }\n        final int negativeButtonText = arguments != null ? arguments.getInt(\"negativeButtonText\", -1) : -1;\n        if (negativeButtonText > 0) {\n            builder.setNegativeButton(negativeButtonText, (dialog, which) -> {\n                if (callback != null) {\n                    callback.onCancel(requestCode);\n                    return;\n                }\n                if (singleCallback != null) {\n                    singleCallback.onCancel(requestCode);\n                }\n            });\n        }\n        if (type == Type.MULTIPLE || type == Type.SINGLE_CHECKED) {\n            final int positiveButtonText = arguments != null ? arguments.getInt(\"positiveButtonText\", -1) : -1;\n            if (positiveButtonText > 0) {\n                builder.setPositiveButton(positiveButtonText, (dialog, which) -> {\n                    if (callback == null || options == null || options.isEmpty()) return;\n                    try {\n                        final List<T> selected = new ArrayList<>();\n                        final SparseBooleanArray checkedItemPositions = ((AlertDialog) dialog).getListView().getCheckedItemPositions();\n                        for (int i = 0; i < checkedItemPositions.size(); i++) {\n                            final int position = checkedItemPositions.keyAt(i);\n                            final boolean checked = checkedItemPositions.get(position);\n                            if (!checked) continue;\n                            //noinspection unchecked\n                            final Option<T> option = (Option<T>) options.get(position);\n                            selected.add(option.value);\n                        }\n                        callback.onMultipleSelect(requestCode, selected);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"onCreateDialog: \", e);\n                    }\n                });\n            }\n        }\n        if (type == Type.MULTIPLE) {\n            if (options != null && !options.isEmpty()) {\n                final String[] items = options.stream()\n                                              .map(option -> option.label)\n                                              .toArray(String[]::new);\n                final boolean[] checkedItems = Booleans.toArray(options.stream()\n                                                                       .map(option -> option.checked)\n                                                                       .collect(Collectors.toList()));\n                builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> {\n                    if (callback == null) return;\n                    try {\n                        final Option<?> option = options.get(which);\n                        //noinspection unchecked\n                        callback.onCheckChange(requestCode, (T) option.value, isChecked);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"onCreateDialog: \", e);\n                    }\n                });\n            }\n        } else {\n            if (options != null && !options.isEmpty()) {\n                final String[] items = options.stream()\n                                              .map(option -> option.label)\n                                              .toArray(String[]::new);\n                if (type == Type.SINGLE_CHECKED) {\n                    int index = -1;\n                    for (int i = 0; i < options.size(); i++) {\n                        if (options.get(i).checked) {\n                            index = i;\n                            break;\n                        }\n                    }\n                    builder.setSingleChoiceItems(items, index, (dialog, which) -> {\n                        if (callback == null) return;\n                        try {\n                            final Option<?> option = options.get(which);\n                            //noinspection unchecked\n                            callback.onCheckChange(requestCode, (T) option.value, true);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"onCreateDialog: \", e);\n                        }\n                    });\n                } else if (type == Type.SINGLE) {\n                    builder.setItems(items, (dialog, which) -> {\n                        if (singleCallback == null) return;\n                        try {\n                            final Option<?> option = options.get(which);\n                            //noinspection unchecked\n                            singleCallback.onSelect(requestCode, (T) option.value);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"onCreateDialog: \", e);\n                        }\n                    });\n                }\n            }\n        }\n        return builder.create();\n    }\n\n    public void setCallback(final MultiOptionDialogCallback<T> callback) {\n        if (callback == null) return;\n        this.callback = callback;\n    }\n\n    public void setSingleCallback(final MultiOptionDialogSingleCallback<T> callback) {\n        if (callback == null) return;\n        this.singleCallback = callback;\n    }\n\n    public interface MultiOptionDialogCallback<T> {\n        void onSelect(int requestCode, T result);\n\n        void onMultipleSelect(int requestCode, List<T> result);\n\n        void onCheckChange(int requestCode, T item, boolean isChecked);\n\n        void onCancel(int requestCode);\n    }\n\n    public interface MultiOptionDialogSingleCallback<T> {\n        void onSelect(int requestCode, T result);\n\n        void onCancel(int requestCode);\n    }\n\n    public static class Option<T extends Serializable> {\n        private final String label;\n        private final T value;\n        private final boolean checked;\n\n        public Option(final String label, final T value) {\n            this.label = label;\n            this.value = value;\n            this.checked = false;\n        }\n\n        public Option(final String label, final T value, final boolean checked) {\n            this.label = label;\n            this.value = value;\n            this.checked = checked;\n        }\n\n        public String getLabel() {\n            return label;\n        }\n\n        public T getValue() {\n            return value;\n        }\n\n        public boolean isChecked() {\n            return checked;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/PostLoadingDialogFragment.kt",
    "content": "package awais.instagrabber.dialogs\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.os.Bundle\nimport android.util.Log\nimport android.widget.Toast\nimport androidx.fragment.app.DialogFragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.fragment.findNavController\nimport awais.instagrabber.R\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.webservices.GraphQLRepository\nimport awais.instagrabber.webservices.MediaRepository\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.util.*\n\nclass PostLoadingDialogFragment : DialogFragment() {\n    private var isLoggedIn: Boolean = false\n\n    private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }\n    private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        var userId: Long = 0\n        var csrfToken: String? = null\n        if (cookie.isNotBlank()) {\n            userId = getUserIdFromCookie(cookie)\n            csrfToken = getCsrfTokenFromCookie(cookie)\n        }\n        if (cookie.isBlank() || userId == 0L || csrfToken.isNullOrBlank()) {\n            isLoggedIn = false\n            return\n        }\n        isLoggedIn = true\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext())\n            .setCancelable(false)\n            .setView(R.layout.dialog_opening_post)\n            .create()\n    }\n\n    override fun onAttach(context: Context) {\n        super.onAttach(context)\n        val arguments = PostLoadingDialogFragmentArgs.fromBundle(arguments ?: return)\n        val shortCode = arguments.shortCode\n        lifecycleScope.launch(Dispatchers.IO) {\n            try {\n                val media = if (isLoggedIn) mediaRepository.fetch(TextUtils.shortcodeToId(shortCode)) else graphQLRepository.fetchPost(shortCode)\n                withContext(Dispatchers.Main) {\n                    if (media == null) {\n                        Toast.makeText(context, R.string.post_not_found, Toast.LENGTH_SHORT).show()\n                        return@withContext\n                    }\n                    try {\n                        findNavController().navigate(PostLoadingDialogFragmentDirections.actionToPost(media, 0))\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"showPostView: \", e)\n                    }\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"showPostView: \", e)\n            } finally {\n                withContext(Dispatchers.Main) {\n                    dismiss()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.kt",
    "content": "package awais.instagrabber.dialogs\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.CompoundButton\nimport androidx.fragment.app.DialogFragment\nimport awais.instagrabber.R\nimport awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding\nimport awais.instagrabber.models.PostsLayoutPreferences\nimport awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType\nimport awais.instagrabber.models.PostsLayoutPreferences.ProfilePicSize\nimport awais.instagrabber.utils.Utils\nimport com.google.android.material.button.MaterialButtonToggleGroup\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\n\nclass PostsLayoutPreferencesDialogFragment(\n    private val layoutPreferenceKey: String,\n    private val onApplyListener: OnApplyListener\n) : DialogFragment() {\n\n    private lateinit var binding: DialogPostLayoutPreferencesBinding\n\n    private val preferencesBuilder: PostsLayoutPreferences.Builder\n\n    init {\n        val preferences = PostsLayoutPreferences.fromJson(Utils.settingsHelper.getString(layoutPreferenceKey))\n        preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences)\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        binding = DialogPostLayoutPreferencesBinding.inflate(layoutInflater)\n        init()\n        return MaterialAlertDialogBuilder(requireContext())\n            .setView(binding.getRoot())\n            .setPositiveButton(R.string.apply) { _: DialogInterface?, _: Int ->\n                val preferences = preferencesBuilder.build()\n                val json = preferences.json\n                Utils.settingsHelper.putString(layoutPreferenceKey, json)\n                onApplyListener.onApply(preferences)\n            }\n            .create()\n    }\n\n    private fun init() {\n        initLayoutToggle()\n        if (preferencesBuilder.type != PostsLayoutType.LINEAR) {\n            initStaggeredOrGridOptions()\n        }\n    }\n\n    private fun initStaggeredOrGridOptions() {\n        initColCountToggle()\n        initNamesToggle()\n        initAvatarsToggle()\n        initCornersToggle()\n        initGapToggle()\n    }\n\n    private fun initLayoutToggle() {\n        val selectedLayoutId = selectedLayoutId\n        binding.layoutToggle.check(selectedLayoutId)\n        if (selectedLayoutId == R.id.layout_linear) {\n            binding.staggeredOrGridOptions.visibility = View.GONE\n        }\n        binding.layoutToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->\n            if (!isChecked) return@addOnButtonCheckedListener\n            when (checkedId) {\n                R.id.layout_linear -> {\n                    preferencesBuilder.type = PostsLayoutType.LINEAR\n                    preferencesBuilder.colCount = 1\n                    binding.staggeredOrGridOptions.visibility = View.GONE\n                }\n                R.id.layout_staggered -> {\n                    preferencesBuilder.type = PostsLayoutType.STAGGERED_GRID\n                    if (preferencesBuilder.colCount == 1) {\n                        preferencesBuilder.colCount = 2\n                    }\n                    binding.staggeredOrGridOptions.visibility = View.VISIBLE\n                    initStaggeredOrGridOptions()\n                }\n                else -> {\n                    preferencesBuilder.type = PostsLayoutType.GRID\n                    if (preferencesBuilder.colCount == 1) {\n                        preferencesBuilder.colCount = 2\n                    }\n                    binding.staggeredOrGridOptions.visibility = View.VISIBLE\n                    initStaggeredOrGridOptions()\n                }\n            }\n        }\n    }\n\n    private fun initColCountToggle() {\n        binding.colCountToggle.check(selectedColCountId)\n        binding.colCountToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->\n            if (!isChecked) return@addOnButtonCheckedListener\n            preferencesBuilder.colCount = (if (checkedId == R.id.col_count_two) 2 else 3)\n        }\n    }\n\n    private fun initAvatarsToggle() {\n        binding.showAvatarToggle.isChecked = preferencesBuilder.isAvatarVisible\n        binding.avatarSizeToggle.check(selectedAvatarSizeId)\n        binding.showAvatarToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->\n            preferencesBuilder.isAvatarVisible = isChecked\n            binding.labelAvatarSize.visibility = if (isChecked) View.VISIBLE else View.GONE\n            binding.avatarSizeToggle.visibility = if (isChecked) View.VISIBLE else View.GONE\n        }\n        binding.labelAvatarSize.visibility = if (preferencesBuilder.isAvatarVisible) View.VISIBLE else View.GONE\n        binding.avatarSizeToggle.visibility = if (preferencesBuilder.isAvatarVisible) View.VISIBLE else View.GONE\n        binding.avatarSizeToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->\n            if (!isChecked) return@addOnButtonCheckedListener\n            preferencesBuilder.profilePicSize = when (checkedId) {\n                R.id.avatar_size_tiny -> ProfilePicSize.TINY\n                R.id.avatar_size_small -> ProfilePicSize.SMALL\n                else -> ProfilePicSize.REGULAR\n            }\n        }\n    }\n\n    private fun initNamesToggle() {\n        binding.showNamesToggle.isChecked = preferencesBuilder.isNameVisible\n        binding.showNamesToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->\n            preferencesBuilder.isNameVisible = isChecked\n        }\n    }\n\n    private fun initCornersToggle() {\n        binding.cornersToggle.check(selectedCornersId)\n        binding.cornersToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->\n            if (!isChecked) return@addOnButtonCheckedListener\n            if (checkedId == R.id.corners_round) {\n                preferencesBuilder.hasRoundedCorners = true\n                return@addOnButtonCheckedListener\n            }\n            preferencesBuilder.hasRoundedCorners = false\n        }\n    }\n\n    private fun initGapToggle() {\n        binding.showGapToggle.isChecked = preferencesBuilder.hasGap\n        binding.showGapToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->\n            preferencesBuilder.hasGap = isChecked\n        }\n    }\n\n    private val selectedLayoutId: Int\n        get() = when (preferencesBuilder.type) {\n            PostsLayoutType.STAGGERED_GRID -> R.id.layout_staggered\n            PostsLayoutType.LINEAR -> R.id.layout_linear\n            PostsLayoutType.GRID -> R.id.layout_grid\n            else -> R.id.layout_grid\n        }\n\n    private val selectedColCountId: Int\n        get() = when (preferencesBuilder.colCount) {\n            2 -> R.id.col_count_two\n            3 -> R.id.col_count_three\n            else -> R.id.col_count_three\n        }\n\n    private val selectedCornersId: Int\n        get() = if (preferencesBuilder.hasRoundedCorners) {\n            R.id.corners_round\n        } else R.id.corners_square\n\n    private val selectedAvatarSizeId: Int\n        get() = when (preferencesBuilder.profilePicSize) {\n            ProfilePicSize.TINY -> R.id.avatar_size_tiny\n            ProfilePicSize.SMALL -> R.id.avatar_size_small\n            ProfilePicSize.REGULAR -> R.id.avatar_size_regular\n            else -> R.id.avatar_size_regular\n        }\n\n    fun interface OnApplyListener {\n        fun onApply(preferences: PostsLayoutPreferences)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.graphics.Color;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.ColorDrawable;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.fragment.app.DialogFragment;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.drawee.interfaces.DraweeController;\nimport com.facebook.imagepipeline.image.ImageInfo;\n\n// import java.io.File;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.customviews.drawee.AnimatedZoomableController;\nimport awais.instagrabber.customviews.drawee.DoubleTapGestureListener;\nimport awais.instagrabber.databinding.DialogProfilepicBinding;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.UserRepository;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class ProfilePicDialogFragment extends DialogFragment {\n    private static final String TAG = \"ProfilePicDlgFragment\";\n\n    private long id;\n    private String name;\n    private String fallbackUrl;\n\n    private boolean isLoggedIn;\n    private DialogProfilepicBinding binding;\n    private String url;\n\n    public static ProfilePicDialogFragment getInstance(final long id, final String name, final String fallbackUrl) {\n        final Bundle args = new Bundle();\n        args.putLong(\"id\", id);\n        args.putString(\"name\", name);\n        args.putString(\"fallbackUrl\", fallbackUrl);\n        final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public ProfilePicDialogFragment() {}\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        binding = DialogProfilepicBinding.inflate(inflater, container, false);\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        return binding.getRoot();\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(final Bundle savedInstanceState) {\n        final Dialog dialog = super.onCreateDialog(savedInstanceState);\n        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);\n        return dialog;\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));\n        int width = ViewGroup.LayoutParams.MATCH_PARENT;\n        int height = ViewGroup.LayoutParams.MATCH_PARENT;\n        window.setLayout(width, height);\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        init();\n        fetchAvatar();\n    }\n\n    private void init() {\n        final Bundle arguments = getArguments();\n        if (arguments == null) {\n            dismiss();\n            return;\n        }\n        id = arguments.getLong(\"id\");\n        name = arguments.getString(\"name\");\n        fallbackUrl = arguments.getString(\"fallbackUrl\");\n        binding.download.setOnClickListener(v -> {\n            final Context context = getContext();\n            if (context == null) return;\n            // if (ContextCompat.checkSelfPermission(context, DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {\n            downloadProfilePicture();\n            // return;\n            // }\n            // requestPermissions(DownloadUtils.PERMS, 8020);\n        });\n    }\n\n    @Override\n    public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n            downloadProfilePicture();\n        }\n    }\n\n    private void fetchAvatar() {\n        if (isLoggedIn) {\n            final UserRepository repository = UserRepository.Companion.getInstance();\n            repository.getUserInfo(id, CoroutineUtilsKt.getContinuation((user, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                if (throwable != null) {\n                    final Context context = getContext();\n                    if (context == null) {\n                        dismiss();\n                        return;\n                    }\n                    Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show();\n                    dismiss();\n                    return;\n                }\n                if (user != null) {\n                    final String url = user.getHDProfilePicUrl();\n                    if (TextUtils.isEmpty(url)) {\n                        final Context context = getContext();\n                        if (context == null) return;\n                        Toast.makeText(context, R.string.no_profile_pic_found, Toast.LENGTH_LONG).show();\n                        return;\n                    }\n                    setupPhoto(url);\n                }\n            }), Dispatchers.getIO()));\n        } else setupPhoto(fallbackUrl);\n    }\n\n    private void setupPhoto(final String result) {\n        if (TextUtils.isEmpty(result)) url = fallbackUrl;\n        else url = result;\n        final DraweeController controller = Fresco\n                .newDraweeControllerBuilder()\n                .setUri(url)\n                .setOldController(binding.imageViewer.getController())\n                .setControllerListener(new BaseControllerListener<ImageInfo>() {\n                    @Override\n                    public void onFailure(final String id, final Throwable throwable) {\n                        super.onFailure(id, throwable);\n                        binding.download.setVisibility(View.GONE);\n                        binding.progressView.setVisibility(View.GONE);\n                    }\n\n                    @Override\n                    public void onFinalImageSet(final String id,\n                                                final ImageInfo imageInfo,\n                                                final Animatable animatable) {\n                        super.onFinalImageSet(id, imageInfo, animatable);\n                        binding.download.setVisibility(View.VISIBLE);\n                        binding.progressView.setVisibility(View.GONE);\n                    }\n                })\n                .build();\n        binding.imageViewer.setController(controller);\n        final AnimatedZoomableController zoomableController = (AnimatedZoomableController) binding.imageViewer.getZoomableController();\n        zoomableController.setMaxScaleFactor(3f);\n        zoomableController.setGestureZoomEnabled(true);\n        zoomableController.setEnabled(true);\n        binding.imageViewer.setZoomingEnabled(true);\n        final DoubleTapGestureListener tapListener = new DoubleTapGestureListener(binding.imageViewer);\n        binding.imageViewer.setTapListener(tapListener);\n    }\n\n    private void downloadProfilePicture() {\n        if (url == null) return;\n        // final File dir = new File(Environment.getExternalStorageDirectory(), \"Download\");\n        final Context context = getContext();\n        if (context == null) return;\n        // if (dir.exists() || dir.mkdirs()) {\n        //\n        // }\n        final String fileName = name + '_' + System.currentTimeMillis() + \".jpg\";\n        // final File saveFile = new File(dir, fileName);\n        final DocumentFile downloadDir = DownloadUtils.getDownloadDir();\n        final DocumentFile saveFile = downloadDir.createFile(Utils.mimeTypeMap.getMimeTypeFromExtension(\"jpg\"), fileName);\n        DownloadUtils.download(context, url, saveFile);\n        // return;\n        // Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.provider.MediaStore;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.inputmethod.InputMethodManager;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\n\nimport awais.instagrabber.databinding.DialogRestoreBackupBinding;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.ExportImportUtils;\nimport awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static android.app.Activity.RESULT_OK;\n\npublic class RestoreBackupDialogFragment extends DialogFragment {\n    private static final String TAG = RestoreBackupDialogFragment.class.getSimpleName();\n    private static final int STORAGE_PERM_REQUEST_CODE = 8020;\n    private static final int OPEN_FILE_REQUEST_CODE = 1;\n\n    private OnResultListener onResultListener;\n\n    private DialogRestoreBackupBinding binding;\n    private boolean isEncrypted;\n    private Uri uri;\n\n    public RestoreBackupDialogFragment() {}\n\n    public RestoreBackupDialogFragment(final OnResultListener onResultListener) {\n        this.onResultListener = onResultListener;\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        binding = DialogRestoreBackupBinding.inflate(inflater, container, false);\n        return binding.getRoot();\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        Dialog dialog = super.onCreateDialog(savedInstanceState);\n        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);\n        return dialog;\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        final int height = ViewGroup.LayoutParams.WRAP_CONTENT;\n        final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);\n        window.setLayout(width, height);\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        init();\n    }\n\n    @Override\n    public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n    }\n\n    @Override\n    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {\n        if (data == null || data.getData() == null) return;\n        if (resultCode != RESULT_OK || requestCode != OPEN_FILE_REQUEST_CODE) return;\n        final Context context = getContext();\n        if (context == null) return;\n        isEncrypted = ExportImportUtils.isEncrypted(context, data.getData());\n        if (isEncrypted) {\n            binding.passwordGroup.setVisibility(View.VISIBLE);\n            binding.passwordGroup.post(() -> {\n                binding.etPassword.requestFocus();\n                binding.etPassword.post(() -> {\n                    final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n                    if (imm == null) return;\n                    imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT);\n                });\n                binding.btnRestore.setEnabled(!TextUtils.isEmpty(binding.etPassword.getText()));\n            });\n        } else {\n            binding.passwordGroup.setVisibility(View.GONE);\n            binding.btnRestore.setEnabled(true);\n        }\n        uri = data.getData();\n        AppExecutors.INSTANCE.getMainThread().execute(() -> {\n            Cursor c = null;\n            try {\n                String[] projection = {MediaStore.Files.FileColumns.DISPLAY_NAME};\n                final ContentResolver contentResolver = context.getContentResolver();\n                c = contentResolver.query(uri, projection, null, null, null);\n                if (c != null) {\n                    while (c.moveToNext()) {\n                        final String displayName = c.getString(0);\n                        binding.filePath.setText(displayName);\n                    }\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"onActivityResult: \", e);\n            } finally {\n                if (c != null) {\n                    c.close();\n                }\n            }\n        });\n    }\n\n    private void init() {\n        final Context context = getContext();\n        if (context == null) return;\n        binding.btnRestore.setEnabled(false);\n        binding.btnRestore.setOnClickListener(v -> new Handler(Looper.getMainLooper()).post(() -> {\n            if (uri == null) return;\n            int flags = 0;\n            if (binding.cbFavorites.isChecked()) {\n                flags |= ExportImportUtils.FLAG_FAVORITES;\n            }\n            if (binding.cbSettings.isChecked()) {\n                flags |= ExportImportUtils.FLAG_SETTINGS;\n            }\n            if (binding.cbAccounts.isChecked()) {\n                flags |= ExportImportUtils.FLAG_COOKIES;\n            }\n            final Editable text = binding.etPassword.getText();\n            if (isEncrypted && text == null) return;\n            try {\n                ExportImportUtils.importData(\n                        context,\n                        flags,\n                        uri,\n                        !isEncrypted ? null : text.toString(),\n                        result -> {\n                            if (onResultListener != null) {\n                                onResultListener.onResult(result);\n                            }\n                            dismiss();\n                        }\n                );\n            } catch (IncorrectPasswordException e) {\n                binding.passwordField.setError(\"Incorrect password\");\n            }\n        }));\n        binding.etPassword.addTextChangedListener(new TextWatcher() {\n            @Override\n            public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {}\n\n            @Override\n            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n                binding.btnRestore.setEnabled(!TextUtils.isEmpty(s));\n                binding.passwordField.setError(null);\n            }\n\n            @Override\n            public void afterTextChanged(final Editable s) {}\n        });\n        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);\n        intent.setType(\"*/*\");\n        startActivityForResult(intent, OPEN_FILE_REQUEST_CODE);\n\n    }\n\n    public interface OnResultListener {\n        void onResult(boolean result);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/TabOrderPreferenceDialogFragment.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.os.Bundle;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.recyclerview.widget.ItemTouchHelper;\nimport androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.DirectUsersAdapter;\nimport awais.instagrabber.adapters.TabsAdapter;\nimport awais.instagrabber.adapters.viewholder.TabViewHolder;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.models.Tab;\nimport awais.instagrabber.utils.NavigationHelperKt;\nimport awais.instagrabber.utils.Utils;\nimport kotlin.Pair;\n\nimport static androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG;\nimport static androidx.recyclerview.widget.ItemTouchHelper.DOWN;\nimport static androidx.recyclerview.widget.ItemTouchHelper.UP;\n\npublic class TabOrderPreferenceDialogFragment extends DialogFragment {\n    private Callback callback;\n    private Context context;\n    private List<Tab> tabsInPref;\n    private ItemTouchHelper itemTouchHelper;\n    private AlertDialog dialog;\n    private List<Tab> newOrderTabs;\n    private List<Tab> newOtherTabs;\n\n    private final TabsAdapter.TabAdapterCallback tabAdapterCallback = new TabsAdapter.TabAdapterCallback() {\n        @Override\n        public void onStartDrag(final TabViewHolder viewHolder) {\n            if (itemTouchHelper == null || viewHolder == null) return;\n            itemTouchHelper.startDrag(viewHolder);\n        }\n\n        @Override\n        public void onOrderChange(final List<Tab> newOrderTabs) {\n            if (newOrderTabs == null || tabsInPref == null || dialog == null) return;\n            TabOrderPreferenceDialogFragment.this.newOrderTabs = newOrderTabs;\n            setSaveButtonState(newOrderTabs);\n        }\n\n        @Override\n        public void onAdd(final Tab tab) {\n            // Add this tab to newOrderTabs\n            newOrderTabs = ImmutableList.<Tab>builder()\n                    .addAll(newOrderTabs)\n                    .add(tab)\n                    .build();\n            // Remove this tab from newOtherTabs\n            if (newOtherTabs != null) {\n                newOtherTabs = newOtherTabs.stream()\n                                           .filter(t -> !t.equals(tab))\n                                           .collect(Collectors.toList());\n            }\n            setSaveButtonState(newOrderTabs);\n            // submit these tab lists to adapter\n            if (adapter == null) return;\n            adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> adapter.notifyDataSetChanged(), 300));\n        }\n\n        @Override\n        public void onRemove(final Tab tab) {\n            // Remove this tab from newOrderTabs\n            newOrderTabs = newOrderTabs.stream()\n                                       .filter(t -> !t.equals(tab))\n                                       .collect(Collectors.toList());\n            // Add this tab to newOtherTabs\n            if (newOtherTabs != null) {\n                newOtherTabs = ImmutableList.<Tab>builder()\n                        .addAll(newOtherTabs)\n                        .add(tab)\n                        .build();\n            }\n            setSaveButtonState(newOrderTabs);\n            // submit these tab lists to adapter\n            if (adapter == null) return;\n            adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> {\n                adapter.notifyDataSetChanged();\n                if (tab.getNavigationRootId() == R.id.direct_messages_nav_graph) {\n                    final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(\n                            111, 0, R.string.dm_remove_warning, R.string.ok, 0, 0\n                    );\n                    dialogFragment.show(getChildFragmentManager(), \"dm_warning_dialog\");\n                }\n            }, 500));\n        }\n\n        private void setSaveButtonState(final List<Tab> newOrderTabs) {\n            dialog.getButton(AlertDialog.BUTTON_POSITIVE)\n                  .setEnabled(!newOrderTabs.equals(tabsInPref));\n        }\n    };\n    private final SimpleCallback simpleCallback = new SimpleCallback(UP | DOWN, 0) {\n        private int movePosition = RecyclerView.NO_POSITION;\n\n        @Override\n        public int getMovementFlags(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.ViewHolder viewHolder) {\n            if (viewHolder instanceof DirectUsersAdapter.HeaderViewHolder) return 0;\n            if (viewHolder instanceof TabViewHolder && !((TabViewHolder) viewHolder).isDraggable()) return 0;\n            return super.getMovementFlags(recyclerView, viewHolder);\n        }\n\n        @Override\n        public void onChildDraw(@NonNull final Canvas c,\n                                @NonNull final RecyclerView recyclerView,\n                                @NonNull final RecyclerView.ViewHolder viewHolder,\n                                final float dX,\n                                final float dY,\n                                final int actionState,\n                                final boolean isCurrentlyActive) {\n            if (actionState != ACTION_STATE_DRAG) {\n                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);\n                return;\n            }\n            final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter();\n            if (adapter == null) {\n                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);\n                return;\n            }\n            // Do not allow dragging into 'Other tabs' category\n            float edgeY = dY;\n            final int lastPosition = adapter.getCurrentCount() - 1;\n            final View view = viewHolder.itemView;\n            // final int topEdge = recyclerView.getTop();\n            final int bottomEdge = view.getHeight() * adapter.getCurrentCount() - view.getBottom();\n            // if (movePosition == 0 && dY < topEdge) {\n            //     edgeY = topEdge;\n            // } else\n            if (movePosition >= lastPosition && dY >= bottomEdge) {\n                edgeY = bottomEdge;\n            }\n            super.onChildDraw(c, recyclerView, viewHolder, dX, edgeY, actionState, isCurrentlyActive);\n        }\n\n        @Override\n        public boolean onMove(@NonNull final RecyclerView recyclerView,\n                              @NonNull final RecyclerView.ViewHolder viewHolder,\n                              @NonNull final RecyclerView.ViewHolder target) {\n            final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter();\n            if (adapter == null) return false;\n            movePosition = target.getBindingAdapterPosition();\n            if (movePosition >= adapter.getCurrentCount()) {\n                return false;\n            }\n            final int from = viewHolder.getBindingAdapterPosition();\n            final int to = target.getBindingAdapterPosition();\n            adapter.moveItem(from, to);\n            // adapter.notifyItemMoved(from, to);\n            return true;\n        }\n\n        @Override\n        public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int direction) {}\n\n        @Override\n        public void onSelectedChanged(@Nullable final RecyclerView.ViewHolder viewHolder, final int actionState) {\n            super.onSelectedChanged(viewHolder, actionState);\n            if (!(viewHolder instanceof TabViewHolder)) {\n                movePosition = RecyclerView.NO_POSITION;\n                return;\n            }\n            if (actionState == ACTION_STATE_DRAG) {\n                ((TabViewHolder) viewHolder).setDragging(true);\n                movePosition = viewHolder.getBindingAdapterPosition();\n            }\n        }\n\n        @Override\n        public void clearView(@NonNull final RecyclerView recyclerView,\n                              @NonNull final RecyclerView.ViewHolder viewHolder) {\n            super.clearView(recyclerView, viewHolder);\n            ((TabViewHolder) viewHolder).setDragging(false);\n            movePosition = RecyclerView.NO_POSITION;\n        }\n    };\n    private TabsAdapter adapter;\n    private RecyclerView list;\n\n    public static TabOrderPreferenceDialogFragment newInstance() {\n        final Bundle args = new Bundle();\n        final TabOrderPreferenceDialogFragment fragment = new TabOrderPreferenceDialogFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public TabOrderPreferenceDialogFragment() {}\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        try {\n            callback = (Callback) getParentFragment();\n        } catch (ClassCastException e) {\n            // throw new ClassCastException(\"Calling fragment must implement TabOrderPreferenceDialogFragment.Callback interface\");\n        }\n        this.context = context;\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {\n        return new MaterialAlertDialogBuilder(context)\n                .setView(createView())\n                .setPositiveButton(R.string.save, (d, w) -> {\n                    final boolean hasChanged = newOrderTabs != null && !newOrderTabs.equals(tabsInPref);\n                    if (hasChanged) {\n                        saveNewOrder();\n                    }\n                    if (callback == null) return;\n                    callback.onSave(hasChanged);\n                })\n                .setNegativeButton(R.string.cancel, (dialog, which) -> {\n                    if (callback == null) return;\n                    callback.onCancel();\n                })\n                .create();\n    }\n\n    private void saveNewOrder() {\n        final String newOrderString = newOrderTabs\n                .stream()\n                .map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))\n                .collect(Collectors.joining(\",\"));\n        Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString);\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (!(dialog instanceof AlertDialog)) return;\n        this.dialog = (AlertDialog) dialog;\n        this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);\n    }\n\n    @NonNull\n    private View createView() {\n        list = new RecyclerView(context);\n        list.setLayoutManager(new LinearLayoutManager(context));\n        itemTouchHelper = new ItemTouchHelper(simpleCallback);\n        itemTouchHelper.attachToRecyclerView(list);\n        adapter = new TabsAdapter(tabAdapterCallback);\n        list.setAdapter(adapter);\n        final Pair<List<Tab>, List<Tab>> navTabListPair = NavigationHelperKt.getLoggedInNavTabs(context);\n        tabsInPref = navTabListPair.getFirst();\n        // initially set newOrderTabs and newOtherTabs same as current tabs\n        newOrderTabs = navTabListPair.getFirst();\n        newOtherTabs = navTabListPair.getSecond();\n        adapter.submitList(navTabListPair.getFirst(), navTabListPair.getSecond());\n        return list;\n    }\n\n    public interface Callback {\n        void onSave(final boolean orderHasChanged);\n\n        void onCancel();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/dialogs/TimeSettingsDialog.java",
    "content": "package awais.instagrabber.dialogs;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.AdapterView;\nimport android.widget.CompoundButton;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.DialogTimeSettingsBinding;\nimport awais.instagrabber.utils.DateUtils;\nimport awais.instagrabber.utils.LocaleUtils;\nimport awais.instagrabber.utils.TextUtils;\n\npublic final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener,\n        View.OnClickListener, TextWatcher {\n    private DialogTimeSettingsBinding binding;\n    private final LocalDateTime magicDate;\n    private DateTimeFormatter currentFormat;\n    private String selectedFormat;\n    private final boolean customDateTimeFormatEnabled;\n    private final String customDateTimeFormat;\n    private final String dateTimeSelection;\n    private final boolean swapDateTimeEnabled;\n    private final OnConfirmListener onConfirmListener;\n\n    public TimeSettingsDialog(final boolean customDateTimeFormatEnabled,\n                              final String customDateTimeFormat,\n                              final String dateTimeSelection,\n                              final boolean swapDateTimeEnabled,\n                              final OnConfirmListener onConfirmListener) {\n        this.customDateTimeFormatEnabled = customDateTimeFormatEnabled;\n        this.customDateTimeFormat = customDateTimeFormat;\n        this.dateTimeSelection = dateTimeSelection;\n        this.swapDateTimeEnabled = swapDateTimeEnabled;\n        this.onConfirmListener = onConfirmListener;\n        magicDate = LocalDateTime.ofInstant(\n                Instant.now(),\n                ZoneId.systemDefault()\n        );\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = DialogTimeSettingsBinding.inflate(inflater, container, false);\n\n        binding.cbCustomFormat.setOnCheckedChangeListener(this);\n        binding.cbCustomFormat.setChecked(customDateTimeFormatEnabled);\n        binding.cbSwapTimeDate.setChecked(swapDateTimeEnabled);\n        binding.customFormatEditText.setText(customDateTimeFormat);\n\n        final String[] dateTimeFormat = dateTimeSelection.split(\";\"); // output = time;separator;date\n        binding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0]));\n        binding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1]));\n        binding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2]));\n\n        binding.cbSwapTimeDate.setOnCheckedChangeListener(this);\n\n        refreshTimeFormat();\n\n        binding.spTimeFormat.setOnItemSelectedListener(this);\n        binding.spDateFormat.setOnItemSelectedListener(this);\n        binding.spSeparator.setOnItemSelectedListener(this);\n\n        binding.customFormatEditText.addTextChangedListener(this);\n        binding.btnConfirm.setOnClickListener(this);\n        binding.customFormatField.setEndIconOnClickListener(this);\n\n        return binding.getRoot();\n    }\n\n    private void refreshTimeFormat() {\n        final boolean isCustom = binding.cbCustomFormat.isChecked();\n        if (isCustom) {\n            final Editable text = binding.customFormatEditText.getText();\n            if (text != null) {\n                selectedFormat = text.toString();\n            }\n        } else {\n            final String sepStr = String.valueOf(binding.spSeparator.getSelectedItem());\n            final String timeStr = String.valueOf(binding.spTimeFormat.getSelectedItem());\n            final String dateStr = String.valueOf(binding.spDateFormat.getSelectedItem());\n\n            final boolean isSwapTime = binding.cbSwapTimeDate.isChecked();\n            final boolean isBlankSeparator = binding.spSeparator.getSelectedItemPosition() <= 0;\n\n            selectedFormat = (isSwapTime ? dateStr : timeStr)\n                    + (isBlankSeparator ? \" \" : \" '\" + sepStr + \"' \")\n                    + (isSwapTime ? timeStr : dateStr);\n        }\n\n        binding.btnConfirm.setEnabled(true);\n        try {\n            currentFormat = DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale());\n            if (isCustom) {\n                final boolean valid = !TextUtils.isEmpty(selectedFormat) && DateUtils.checkFormatterValid(currentFormat);\n                binding.customFormatField.setError(valid ? null :getString(R.string.invalid_format));\n                if (!valid) {\n                    binding.btnConfirm.setEnabled(false);\n                }\n            }\n            binding.timePreview.setText(magicDate.format(currentFormat));\n        } catch (Exception e) {\n            binding.btnConfirm.setEnabled(false);\n            binding.timePreview.setText(null);\n        }\n    }\n\n    @Override\n    public void onItemSelected(final AdapterView<?> p, final View v, final int pos, final long id) {\n        refreshTimeFormat();\n    }\n\n    @Override\n    public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {\n        if (buttonView == binding.cbCustomFormat) {\n            binding.customFormatField.setVisibility(isChecked ? View.VISIBLE : View.GONE);\n            binding.customFormatField.setEnabled(isChecked);\n\n            binding.spTimeFormat.setEnabled(!isChecked);\n            binding.spDateFormat.setEnabled(!isChecked);\n            binding.spSeparator.setEnabled(!isChecked);\n            binding.cbSwapTimeDate.setEnabled(!isChecked);\n        }\n        refreshTimeFormat();\n    }\n\n    @Override\n    public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n        refreshTimeFormat();\n    }\n\n    @Override\n    public void onClick(final View v) {\n        if (v == binding.btnConfirm) {\n            if (onConfirmListener != null) {\n                onConfirmListener.onConfirm(\n                        binding.cbCustomFormat.isChecked(),\n                        binding.spTimeFormat.getSelectedItemPosition(),\n                        binding.spSeparator.getSelectedItemPosition(),\n                        binding.spDateFormat.getSelectedItemPosition(),\n                        selectedFormat,\n                        binding.cbSwapTimeDate.isChecked());\n            }\n            dismiss();\n        } else if (v == binding.customFormatField.findViewById(R.id.text_input_end_icon)) {\n            binding.customPanel.setVisibility(\n                    binding.customPanel.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE\n            );\n\n        }\n    }\n\n    public interface OnConfirmListener {\n        void onConfirm(boolean isCustomFormat,\n                       int spTimeFormatSelectedItemPosition,\n                       int spSeparatorSelectedItemPosition,\n                       int spDateFormatSelectedItemPosition,\n                       final String selectedFormat,\n                       final boolean swapDateTime);\n    }\n\n    @Override\n    public void onNothingSelected(final AdapterView<?> parent) { }\n\n    @Override\n    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { }\n\n    @Override\n    public void afterTextChanged(final Editable s) { }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final Window window = dialog.getWindow();\n        if (window == null) return;\n        final WindowManager.LayoutParams params = window.getAttributes();\n        params.width = ViewGroup.LayoutParams.MATCH_PARENT;\n        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;\n        window.setAttributes(params);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.animation.ArgbEvaluator;\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.GradientDrawable;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.graphics.ColorUtils;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\nimport androidx.transition.ChangeBounds;\nimport androidx.transition.TransitionInflater;\nimport androidx.transition.TransitionSet;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.controller.BaseControllerListener;\nimport com.facebook.drawee.interfaces.DraweeController;\nimport com.facebook.imagepipeline.image.ImageInfo;\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.asyncs.SavedPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentCollectionPostsBinding;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.PostItemType;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.saved.SavedCollection;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.CollectionService;\nimport awais.instagrabber.webservices.ServiceCallback;\n\npublic class CollectionPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"CollectionPostsFragment\";\n\n    private MainActivity fragmentActivity;\n    private FragmentCollectionPostsBinding binding;\n    private CoordinatorLayout root;\n    private boolean shouldRefresh = true;\n    private SavedCollection savedCollection;\n    private ActionMode actionMode;\n    private Set<Media> selectedFeedModels;\n    private CollectionService collectionService;\n    private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_SAVED_POSTS_LAYOUT);\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.posts.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.saved_collection_select_menu, new PrimaryActionModeCallback.CallbacksHelper() {\n        @Override\n        public void onDestroy(final ActionMode mode) {\n            binding.posts.endSelection();\n        }\n\n        @Override\n        public boolean onActionItemClicked(final ActionMode mode,\n                                           final MenuItem item) {\n            if (item.getItemId() == R.id.action_download) {\n                if (CollectionPostsFragment.this.selectedFeedModels == null) return false;\n                final Context context = getContext();\n                if (context == null) return false;\n                DownloadUtils.download(context, ImmutableList.copyOf(CollectionPostsFragment.this.selectedFeedModels));\n                binding.posts.endSelection();\n            }\n            return false;\n        }\n    });\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            try {\n                final NavDirections commentsAction = CollectionPostsFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getPk(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = CollectionPostsFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media feedModel) {\n            final Location location = feedModel.getLocation();\n            if (location == null) return;\n            try {\n                final NavDirections action = CollectionPostsFragmentDirections.actionToLocation(location.getPk());\n                NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(final Media feedModel, final int position) {\n            try {\n                final NavDirections action = CollectionPostsFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n        }\n    };\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            CollectionPostsFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        final TransitionSet transitionSet = new TransitionSet();\n        final Context context = getContext();\n        if (context == null) return;\n        transitionSet.addTransition(new ChangeBounds())\n                     .addTransition(TransitionInflater.from(context).inflateTransition(android.R.transition.move))\n                     .setDuration(200);\n        setSharedElementEnterTransition(transitionSet);\n        postponeEnterTransition();\n        setHasOptionsMenu(true);\n        final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);\n        final long userId = CookieUtils.getUserIdFromCookie(cookie);\n        final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);\n        final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        collectionService = CollectionService.getInstance(deviceUuid, csrfToken, userId);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             @Nullable final ViewGroup container,\n                             @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentCollectionPostsBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.collection_posts_menu, menu);\n        final MenuItem deleteMenu = menu.findItem(R.id.delete);\n        if (deleteMenu != null)\n            deleteMenu.setVisible(savedCollection.getCollectionType().equals(\"MEDIA\"));\n        final MenuItem editMenu = menu.findItem(R.id.edit);\n        if (editMenu != null)\n            editMenu.setVisible(savedCollection.getCollectionType().equals(\"MEDIA\"));\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        } else if (item.getItemId() == R.id.delete) {\n            final Context context = getContext();\n            if (context == null) return false;\n            new AlertDialog.Builder(context)\n                    .setTitle(R.string.are_you_sure)\n                    .setMessage(R.string.delete_collection_note)\n                    .setPositiveButton(R.string.confirm, (d, w) -> collectionService.deleteCollection(\n                            savedCollection.getCollectionId(),\n                            new ServiceCallback<String>() {\n                                @Override\n                                public void onSuccess(final String result) {\n                                    SavedCollectionsFragment.pleaseRefresh = true;\n                                    NavHostFragment.findNavController(CollectionPostsFragment.this).navigateUp();\n                                }\n\n                                @Override\n                                public void onFailure(final Throwable t) {\n                                    Log.e(TAG, \"Error deleting collection\", t);\n                                    try {\n                                        final Context context = getContext();\n                                        if (context == null) return;\n                                        Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n                                    } catch (final Throwable ignored) {}\n                                }\n                            }))\n                    .setNegativeButton(R.string.cancel, null)\n                    .show();\n        } else if (item.getItemId() == R.id.edit) {\n            final Context context = getContext();\n            if (context == null) return false;\n            final EditText input = new EditText(context);\n            new AlertDialog.Builder(context)\n                    .setTitle(R.string.edit_collection)\n                    .setView(input)\n                    .setPositiveButton(R.string.confirm, (d, w) -> collectionService.editCollectionName(\n                            savedCollection.getCollectionId(),\n                            input.getText().toString(),\n                            new ServiceCallback<String>() {\n                                @Override\n                                public void onSuccess(final String result) {\n                                    binding.collapsingToolbarLayout.setTitle(input.getText().toString());\n                                    SavedCollectionsFragment.pleaseRefresh = true;\n                                }\n\n                                @Override\n                                public void onFailure(final Throwable t) {\n                                    Log.e(TAG, \"Error editing collection\", t);\n                                    try {\n                                        final Context context = getContext();\n                                        if (context == null) return;\n                                        Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n                                    } catch (final Throwable ignored) {}\n                                }\n                            }))\n                    .setNegativeButton(R.string.cancel, null)\n                    .show();\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        fragmentActivity.setToolbar(binding.toolbar, this);\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.posts.refresh();\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        fragmentActivity.resetToolbar(this);\n    }\n\n    private void init() {\n        if (getArguments() == null) return;\n        final CollectionPostsFragmentArgs fragmentArgs = CollectionPostsFragmentArgs.fromBundle(getArguments());\n        savedCollection = fragmentArgs.getSavedCollection();\n        setupToolbar(fragmentArgs.getTitleColor(), fragmentArgs.getBackgroundColor());\n        setupPosts();\n    }\n\n    private void setupToolbar(final int titleColor, final int backgroundColor) {\n        if (savedCollection == null) {\n            return;\n        }\n        binding.cover.setTransitionName(\"collection-\" + savedCollection.getCollectionId());\n        fragmentActivity.setToolbar(binding.toolbar, this);\n        binding.collapsingToolbarLayout.setTitle(savedCollection.getCollectionName());\n        final int collapsedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0xFF);\n        final int expandedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0x99);\n        binding.collapsingToolbarLayout.setExpandedTitleColor(expandedTitleTextColor);\n        binding.collapsingToolbarLayout.setCollapsedTitleTextColor(collapsedTitleTextColor);\n        binding.collapsingToolbarLayout.setContentScrimColor(backgroundColor);\n        final Drawable navigationIcon = binding.toolbar.getNavigationIcon();\n        final Drawable overflowIcon = binding.toolbar.getOverflowIcon();\n        if (navigationIcon != null && overflowIcon != null) {\n            final Drawable navDrawable = navigationIcon.mutate();\n            final Drawable overflowDrawable = overflowIcon.mutate();\n            navDrawable.setAlpha(0xFF);\n            overflowDrawable.setAlpha(0xFF);\n            final ArgbEvaluator argbEvaluator = new ArgbEvaluator();\n            binding.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {\n                final int totalScrollRange = appBarLayout.getTotalScrollRange();\n                final float current = totalScrollRange + verticalOffset;\n                final float fraction = current / totalScrollRange;\n                final int tempColor = (int) argbEvaluator.evaluate(fraction, collapsedTitleTextColor, expandedTitleTextColor);\n                navDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);\n                overflowDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);\n\n            });\n        }\n        final GradientDrawable gd = new GradientDrawable(\n                GradientDrawable.Orientation.TOP_BOTTOM,\n                new int[]{Color.TRANSPARENT, backgroundColor});\n        binding.background.setBackground(gd);\n        setupCover();\n    }\n\n    private void setupCover() {\n        final Media coverMedia = savedCollection.getCoverMediaList() == null\n                ? savedCollection.getCoverMedia()\n                : savedCollection.getCoverMediaList().get(0);\n        final String coverUrl = ResponseBodyUtils.getImageUrl(coverMedia);\n        final DraweeController controller = Fresco\n                .newDraweeControllerBuilder()\n                .setOldController(binding.cover.getController())\n                .setUri(coverUrl)\n                .setControllerListener(new BaseControllerListener<ImageInfo>() {\n\n                    @Override\n                    public void onFailure(final String id, final Throwable throwable) {\n                        super.onFailure(id, throwable);\n                        startPostponedEnterTransition();\n                    }\n\n                    @Override\n                    public void onFinalImageSet(final String id,\n                                                @Nullable final ImageInfo imageInfo,\n                                                @Nullable final Animatable animatable) {\n                        startPostponedEnterTransition();\n                    }\n                })\n                .build();\n        binding.cover.setController(controller);\n    }\n\n    private void setupPosts() {\n        binding.posts.setViewModelStoreOwner(this)\n                     .setLifeCycleOwner(this)\n                     .setPostFetchService(new SavedPostFetchService(0, PostItemType.COLLECTION, true, savedCollection.getCollectionId()))\n                     .setLayoutPreferences(layoutPreferences)\n                     .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                     .setFeedItemCallback(feedItemCallback)\n                     .setSelectionModeCallback(selectionModeCallback)\n                     .init();\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() ->\n            binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())\n        );\n    }\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = CollectionPostsFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                Constants.PREF_SAVED_POSTS_LAYOUT,\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);\n                });\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.kt",
    "content": "package awais.instagrabber.fragments\n\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport androidx.navigation.fragment.findNavController\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport awais.instagrabber.R\nimport awais.instagrabber.adapters.FavoritesAdapter\nimport awais.instagrabber.databinding.FragmentFavoritesBinding\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.viewmodels.FavoritesViewModel\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\n\nclass FavoritesFragment : Fragment() {\n    private var shouldRefresh = true\n\n    private lateinit var binding: FragmentFavoritesBinding\n    private lateinit var root: RecyclerView\n    private lateinit var adapter: FavoritesAdapter\n\n    private val favoritesViewModel: FavoritesViewModel by viewModels()\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {\n        if (this::root.isInitialized) {\n            shouldRefresh = false\n            return root\n        }\n        binding = FragmentFavoritesBinding.inflate(layoutInflater)\n        root = binding.root\n        binding.favoriteList.layoutManager = LinearLayoutManager(context)\n        return root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        if (!shouldRefresh) return\n        init()\n        shouldRefresh = false\n    }\n\n    override fun onPause() {\n        super.onPause()\n        adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT\n    }\n\n    override fun onResume() {\n        super.onResume()\n        if (!this::adapter.isInitialized) return\n        // refresh list every time in onViewStateRestored since it is cheaper than implementing pull down to refresh\n        favoritesViewModel.list.observe(viewLifecycleOwner, { list: List<Favorite?>? ->\n            adapter.submitList(list) {\n                adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.ALLOW\n            }\n        })\n    }\n\n    private fun init() {\n        adapter = FavoritesAdapter({ model: Favorite ->\n            when (model.type) {\n                FavoriteType.USER -> {\n                    try {\n                        val username = model.query ?: return@FavoritesAdapter\n                        val actionToProfile = FavoritesFragmentDirections.actionToProfile().apply { this.username = username }\n                        findNavController().navigate(actionToProfile)\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"init: \", e)\n                    }\n                }\n                FavoriteType.LOCATION -> {\n                    try {\n                        val locationId = model.query ?: return@FavoritesAdapter\n                        val actionToLocation = FavoritesFragmentDirections.actionToLocation(locationId.toLong())\n                        findNavController().navigate(actionToLocation)\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"init: \", e)\n                    }\n                }\n                FavoriteType.HASHTAG -> {\n                    try {\n                        val hashtag = model.query ?: return@FavoritesAdapter\n                        val actionToHashtag = FavoritesFragmentDirections.actionToHashtag(hashtag)\n                        findNavController().navigate(actionToHashtag)\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"init: \", e)\n                    }\n                }\n                else -> {\n                }\n            }\n        }, { model: Favorite ->\n            // delete\n            val context = context ?: return@FavoritesAdapter false\n            MaterialAlertDialogBuilder(context)\n                .setMessage(getString(R.string.quick_access_confirm_delete, model.query))\n                .setPositiveButton(R.string.yes) { d: DialogInterface, _: Int -> favoritesViewModel.delete(model) { d.dismiss() } }\n                .setNegativeButton(R.string.no, null)\n                .show()\n            true\n        })\n        binding.favoriteList.adapter = adapter\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.kt",
    "content": "package awais.instagrabber.fragments\n\nimport android.os.Bundle\nimport android.view.*\nimport androidx.appcompat.app.ActionBar\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.SearchView\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.navigation.fragment.findNavController\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout\nimport awais.instagrabber.R\nimport awais.instagrabber.adapters.FollowAdapter\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoader\nimport awais.instagrabber.databinding.FragmentFollowersViewerBinding\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.viewmodels.FollowViewModel\nimport thoughtbot.expandableadapter.ExpandableGroup\nimport java.util.*\n\nclass FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {\n    private var isFollowersList = false\n    private var isCompare = false\n    private var shouldRefresh = true\n    private var searching = false\n    private var username: String? = null\n    private var namePost: String? = null\n    private var type = 0\n    private var root: SwipeRefreshLayout? = null\n    private var adapter: FollowAdapter? = null\n    private lateinit var lazyLoader: RecyclerLazyLoader\n    private lateinit var fragmentActivity: AppCompatActivity\n    private lateinit var viewModel: FollowViewModel\n    private lateinit var binding: FragmentFollowersViewerBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        fragmentActivity = activity as AppCompatActivity\n        viewModel = ViewModelProvider(this).get(FollowViewModel::class.java)\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        if (root != null) {\n            shouldRefresh = false\n            return root!!\n        }\n        binding = FragmentFollowersViewerBinding.inflate(layoutInflater)\n        root = binding.root\n        return root!!\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        if (!shouldRefresh) return\n        init()\n        shouldRefresh = false\n    }\n\n    private fun init() {\n        val args = arguments ?: return\n        val fragmentArgs = FollowViewerFragmentArgs.fromBundle(args)\n        viewModel.userId.value = fragmentArgs.profileId\n        isFollowersList = fragmentArgs.isFollowersList\n        username = fragmentArgs.username\n        namePost = username\n        setTitle(username)\n        binding.swipeRefreshLayout.setOnRefreshListener(this)\n        if (isCompare) listCompare() else listFollows()\n        viewModel.fetch(isFollowersList, null)\n    }\n\n    override fun onResume() {\n        super.onResume()\n        setTitle(username)\n        setSubtitle(type)\n    }\n\n    private fun setTitle(title: String?) {\n        val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return\n        actionBar.title = title\n    }\n\n    private fun setSubtitle(subtitleRes: Int) {\n        val actionBar: ActionBar = fragmentActivity.supportActionBar ?: return\n        actionBar.setSubtitle(subtitleRes)\n    }\n\n    override fun onRefresh() {\n        lazyLoader.resetState()\n        viewModel.clearProgress()\n        if (isCompare) listCompare()\n        else viewModel.fetch(isFollowersList, null)\n    }\n\n    override fun onDestroy() {\n        fragmentActivity.supportActionBar?.subtitle = null\n        super.onDestroy()\n    }\n\n    private fun listFollows() {\n        viewModel.comparison.removeObservers(viewLifecycleOwner)\n        viewModel.status.removeObservers(viewLifecycleOwner)\n        type = if (isFollowersList) R.string.followers_type_followers else R.string.followers_type_following\n        setSubtitle(type)\n        val layoutManager = LinearLayoutManager(context)\n        lazyLoader = RecyclerLazyLoader(layoutManager) { _, totalItemsCount ->\n            binding.swipeRefreshLayout.isRefreshing = true\n            val liveData = if (searching) viewModel.search(isFollowersList)\n            else viewModel.fetch(isFollowersList, null)\n            liveData.observe(viewLifecycleOwner) {\n                binding.swipeRefreshLayout.isRefreshing = it.status != Resource.Status.SUCCESS\n                layoutManager.scrollToPosition(totalItemsCount)\n            }\n        }\n        binding.rvFollow.addOnScrollListener(lazyLoader)\n        binding.rvFollow.layoutManager = layoutManager\n        viewModel.getList(isFollowersList).observe(viewLifecycleOwner) {\n            binding.swipeRefreshLayout.isRefreshing = false\n            refreshAdapter(it, null, null, null)\n        }\n    }\n\n    private fun listCompare() {\n        viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner)\n        binding.rvFollow.clearOnScrollListeners()\n        binding.swipeRefreshLayout.isRefreshing = true\n        setSubtitle(R.string.followers_compare)\n        viewModel.status.observe(viewLifecycleOwner) {}\n        viewModel.comparison.observe(viewLifecycleOwner) {\n            if (it != null) {\n                binding.swipeRefreshLayout.isRefreshing = false\n                refreshAdapter(null, it.first, it.second, it.third)\n            }\n        }\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        inflater.inflate(R.menu.follow, menu)\n        val menuSearch = menu.findItem(R.id.action_search)\n        val searchView = menuSearch.actionView as SearchView\n        searchView.queryHint = resources.getString(R.string.action_search)\n        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {\n            override fun onQueryTextSubmit(query: String): Boolean {\n                return false\n            }\n\n            override fun onQueryTextChange(query: String): Boolean {\n                if (query.isEmpty()) {\n                    if (!isCompare && searching) {\n                        viewModel.setQuery(null, isFollowersList)\n                        viewModel.getSearch().removeObservers(viewLifecycleOwner)\n                        viewModel.getList(isFollowersList).observe(viewLifecycleOwner) {\n                            refreshAdapter(it, null, null, null)\n                        }\n                    }\n                    searching = false\n                    return true\n                }\n                searching = true\n                if (isCompare && adapter != null) {\n                    adapter!!.filter.filter(query)\n                    return true\n                }\n                viewModel.getList(isFollowersList).removeObservers(viewLifecycleOwner)\n                binding.swipeRefreshLayout.isRefreshing = true\n                viewModel.setQuery(query, isFollowersList)\n                viewModel.getSearch().observe(viewLifecycleOwner) {\n                    binding.swipeRefreshLayout.isRefreshing = false\n                    refreshAdapter(it, null, null, null)\n                }\n                return true\n            }\n        })\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        if (item.itemId != R.id.action_compare) return super.onOptionsItemSelected(item)\n        binding.rvFollow.adapter = null\n        if (isCompare) {\n            isCompare = false\n            listFollows()\n        } else {\n            isCompare = true\n            listCompare()\n        }\n        return true\n    }\n\n    private fun refreshAdapter(\n        followModels: List<User>?,\n        allFollowing: List<User>?,\n        followingModels: List<User>?,\n        followersModels: List<User>?\n    ) {\n        val groups: ArrayList<ExpandableGroup> = ArrayList<ExpandableGroup>(1)\n        if (isCompare && followingModels != null && followersModels != null && allFollowing != null) {\n            if (followingModels.isNotEmpty()) groups.add(\n                ExpandableGroup(\n                    getString(\n                        R.string.followers_not_following,\n                        username\n                    ), followingModels\n                )\n            )\n            if (followersModels.isNotEmpty()) groups.add(\n                ExpandableGroup(\n                    getString(\n                        R.string.followers_not_follower,\n                        namePost\n                    ), followersModels\n                )\n            )\n            if (allFollowing.isNotEmpty()) groups.add(\n                ExpandableGroup(\n                    getString(R.string.followers_both_following),\n                    allFollowing\n                )\n            )\n        } else if (followModels != null) {\n            groups.add(ExpandableGroup(getString(type), followModels))\n        } else return\n        adapter = FollowAdapter({ v ->\n            val tag = v.tag\n            if (tag is User) {\n                findNavController().navigate(FollowViewerFragmentDirections.actionToProfile().setUsername(tag.username))\n            }\n        }, groups).also {\n            it.toggleGroup(0)\n            binding.rvFollow.adapter = it\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.text.SpannableStringBuilder;\nimport android.text.style.RelativeSizeSpan;\nimport android.text.style.StyleSpan;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.android.material.snackbar.BaseTransientBottomBar;\nimport com.google.android.material.snackbar.Snackbar;\nimport com.google.common.collect.ImmutableList;\n\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.asyncs.HashtagPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentHashtagBinding;\nimport awais.instagrabber.databinding.LayoutHashtagDetailsBinding;\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.db.repositories.FavoriteRepository;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.models.enums.FollowingType;\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport awais.instagrabber.webservices.TagsService;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"HashTagFragment\";\n\n    private MainActivity fragmentActivity;\n    private FragmentHashtagBinding binding;\n    private CoordinatorLayout root;\n    private boolean shouldRefresh = true;\n    private boolean opening = false;\n    private String hashtag;\n    private Hashtag hashtagModel = null;\n    private ActionMode actionMode;\n    //    private StoriesRepository storiesRepository;\n    private boolean isLoggedIn;\n    private TagsService tagsService;\n    private GraphQLRepository graphQLRepository;\n    //    private boolean storiesFetching;\n    private Set<Media> selectedFeedModels;\n    private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_HASHTAG_POSTS_LAYOUT);\n    private LayoutHashtagDetailsBinding hashtagDetailsBinding;\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.posts.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.multi_select_download_menu,\n            new PrimaryActionModeCallback.CallbacksHelper() {\n                @Override\n                public void onDestroy(final ActionMode mode) {\n                    binding.posts.endSelection();\n                }\n\n                @Override\n                public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n                    if (item.getItemId() == R.id.action_download) {\n                        if (HashTagFragment.this.selectedFeedModels == null) return false;\n                        final Context context = getContext();\n                        if (context == null) return false;\n                        DownloadUtils.download(context, ImmutableList.copyOf(HashTagFragment.this.selectedFeedModels));\n                        binding.posts.endSelection();\n                        return true;\n                    }\n                    return false;\n                }\n            });\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            try {\n                final NavDirections commentsAction = HashTagFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getCode(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(HashTagFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = HashTagFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(HashTagFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media media) {\n            final Location location = media.getLocation();\n            if (location == null) return;\n            try {\n                final NavDirections action = HashTagFragmentDirections.actionToLocation(location.getPk());\n                NavHostFragment.findNavController(HashTagFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(@NonNull final Media feedModel, final int position) {\n            if (opening) return;\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            if (TextUtils.isEmpty(user.getUsername())) {\n                // this only happens for anons\n                opening = true;\n                final String code = feedModel.getCode();\n                if (code == null) return;\n                graphQLRepository.fetchPost(code, CoroutineUtilsKt.getContinuation((media, throwable) -> {\n                    opening = false;\n                    if (throwable != null) {\n                        Log.e(TAG, \"Error\", throwable);\n                        return;\n                    }\n                    if (media == null) return;\n                    AppExecutors.INSTANCE.getMainThread().execute(() -> openPostDialog(media, position));\n                }, Dispatchers.getIO()));\n                return;\n            }\n            opening = true;\n            try {\n                final NavDirections action = HashTagFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(HashTagFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n            opening = false;\n        }\n    };\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            HashTagFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n    private final ServiceCallback<Hashtag> cb = new ServiceCallback<Hashtag>() {\n        @Override\n        public void onSuccess(final Hashtag result) {\n            hashtagModel = result;\n            binding.swipeRefreshLayout.setRefreshing(false);\n            setHashtagDetails();\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            setHashtagDetails();\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        tagsService = isLoggedIn ? TagsService.getInstance() : null;\n        //        storiesRepository = isLoggedIn ? StoriesRepository.Companion.getInstance() : null;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n        setHasOptionsMenu(true);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentHashtagBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        hashtagDetailsBinding = binding.header;\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.posts.refresh();\n        //        fetchStories();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        fragmentActivity.setToolbar(binding.toolbar, this);\n        setTitle();\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.topic_posts_menu, menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        fragmentActivity.resetToolbar(this);\n    }\n\n    private void init() {\n        if (getArguments() == null) return;\n        final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());\n        hashtag = fragmentArgs.getHashtag();\n        if (hashtag.charAt(0) == '#') hashtag = hashtag.substring(1);\n        fetchHashtagModel();\n    }\n\n    private void fetchHashtagModel() {\n        binding.swipeRefreshLayout.setRefreshing(true);\n        if (isLoggedIn) tagsService.fetch(hashtag, cb);\n        else graphQLRepository.fetchTag(hashtag, CoroutineUtilsKt.getContinuation((hashtag1, throwable) -> {\n            if (throwable != null) {\n                cb.onFailure(throwable);\n                return;\n            }\n            AppExecutors.INSTANCE.getMainThread().execute(() -> cb.onSuccess(hashtag1));\n        }, Dispatchers.getIO()));\n    }\n\n    private void setupPosts() {\n        binding.posts.setViewModelStoreOwner(this)\n                     .setLifeCycleOwner(this)\n                     .setPostFetchService(new HashtagPostFetchService(hashtagModel, isLoggedIn))\n                     .setLayoutPreferences(layoutPreferences)\n                     .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                     .setFeedItemCallback(feedItemCallback)\n                     .setSelectionModeCallback(selectionModeCallback)\n                     .init();\n        // binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() {\n        //     @Override\n        //     public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {\n        //         super.onScrolled(recyclerView, dx, dy);\n        //         final boolean canScrollVertically = recyclerView.canScrollVertically(-1);\n        //         final MotionScene.Transition transition = root.getTransition(R.id.transition);\n        //         if (transition != null) {\n        //             transition.setEnable(!canScrollVertically);\n        //         }\n        //     }\n        // });\n    }\n\n    private void setHashtagDetails() {\n        if (hashtagModel == null) {\n            try {\n                Toast.makeText(getContext(), R.string.error_loading_hashtag, Toast.LENGTH_SHORT).show();\n                binding.swipeRefreshLayout.setEnabled(false);\n            } catch (Exception ignored) {}\n            return;\n        }\n        setTitle();\n        setupPosts();\n        if (isLoggedIn) {\n            hashtagDetailsBinding.btnFollowTag.setVisibility(View.VISIBLE);\n            hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() == FollowingType.FOLLOWING\n                                                       ? R.string.unfollow\n                                                       : R.string.follow);\n            hashtagDetailsBinding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing() == FollowingType.FOLLOWING\n                                                                   ? R.drawable.ic_outline_person_add_disabled_24\n                                                                   : R.drawable.ic_outline_person_add_24);\n            hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> {\n                final String cookie = settingsHelper.getString(Constants.COOKIE);\n                final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n                final long userId = CookieUtils.getUserIdFromCookie(cookie);\n                final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);\n                if (csrfToken != null && userId != 0) {\n                    hashtagDetailsBinding.btnFollowTag.setClickable(false);\n                    tagsService.changeFollow(\n                            hashtagModel.getFollowing() == FollowingType.FOLLOWING ? \"unfollow\" : \"follow\",\n                            hashtag,\n                            csrfToken,\n                            userId,\n                            deviceUuid,\n                            new ServiceCallback<Boolean>() {\n                                @Override\n                                public void onSuccess(final Boolean result) {\n                                    hashtagDetailsBinding.btnFollowTag.setClickable(true);\n                                    if (!result) {\n                                        Log.e(TAG, \"onSuccess: result is false\");\n                                        Snackbar.make(root, R.string.downloader_unknown_error, BaseTransientBottomBar.LENGTH_LONG)\n                                                .show();\n                                        return;\n                                    }\n                                    hashtagDetailsBinding.btnFollowTag.setText(R.string.unfollow);\n                                    hashtagDetailsBinding.btnFollowTag.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24);\n                                }\n\n                                @Override\n                                public void onFailure(@NonNull final Throwable t) {\n                                    hashtagDetailsBinding.btnFollowTag.setClickable(true);\n                                    Log.e(TAG, \"onFailure: \", t);\n                                    final String message = t.getMessage();\n                                    Snackbar.make(\n                                            root,\n                                            message != null ? message : getString(R.string.downloader_unknown_error),\n                                            BaseTransientBottomBar.LENGTH_LONG)\n                                            .show();\n                                }\n                            });\n                }\n            });\n        } else {\n            hashtagDetailsBinding.btnFollowTag.setVisibility(View.GONE);\n        }\n        hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE);\n        final Context context = getContext();\n        if (context == null) return;\n        final FavoriteRepository favoriteRepository = FavoriteRepository.Companion.getInstance(context);\n        favoriteRepository.getFavorite(\n                hashtag,\n                FavoriteType.HASHTAG,\n                CoroutineUtilsKt.getContinuation((favorite, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null || favorite == null) {\n                        hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);\n                        hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites);\n                        return;\n                    }\n                    favoriteRepository.insertOrUpdateFavorite(\n                            new Favorite(\n                                    favorite.getId(),\n                                    hashtag,\n                                    FavoriteType.HASHTAG,\n                                    hashtagModel.getName(),\n                                    \"res:/\" + R.drawable.ic_hashtag,\n                                    favorite.getDateAdded()\n                            ),\n                            CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                if (throwable1 != null) {\n                                    Log.e(TAG, \"onSuccess: \", throwable1);\n                                    return;\n                                }\n                                hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);\n                                hashtagDetailsBinding.favChip.setText(R.string.favorite_short);\n                            }), Dispatchers.getIO())\n                    );\n                }), Dispatchers.getIO())\n        );\n        hashtagDetailsBinding.favChip.setOnClickListener(v -> favoriteRepository.getFavorite(\n                hashtag,\n                FavoriteType.HASHTAG,\n                CoroutineUtilsKt.getContinuation((favorite, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"setHashtagDetails: \", throwable);\n                        return;\n                    }\n                    if (favorite == null) {\n                        favoriteRepository.insertOrUpdateFavorite(\n                                new Favorite(\n                                        0,\n                                        hashtag,\n                                        FavoriteType.HASHTAG,\n                                        hashtagModel.getName(),\n                                        \"res:/\" + R.drawable.ic_hashtag,\n                                        LocalDateTime.now()\n                                ),\n                                CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                    if (throwable1 != null) {\n                                        Log.e(TAG, \"onDataNotAvailable: \", throwable1);\n                                        return;\n                                    }\n                                    hashtagDetailsBinding.favChip.setText(R.string.favorite_short);\n                                    hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);\n                                    showSnackbar(getString(R.string.added_to_favs));\n                                }), Dispatchers.getIO())\n                        );\n                        return;\n                    }\n                    favoriteRepository.deleteFavorite(\n                            hashtag,\n                            FavoriteType.HASHTAG,\n                            CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                if (throwable1 != null) {\n                                    Log.e(TAG, \"onSuccess: \", throwable1);\n                                    return;\n                                }\n                                hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites);\n                                hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);\n                                showSnackbar(getString(R.string.removed_from_favs));\n                            }), Dispatchers.getIO())\n                    );\n                }), Dispatchers.getIO())\n                                                         )\n        );\n        hashtagDetailsBinding.mainHashtagImage.setImageURI(\"res:/\" + R.drawable.ic_hashtag);\n        final String postCount = String.valueOf(hashtagModel.getMediaCount());\n        final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(\n                R.plurals.main_posts_count_inline,\n                hashtagModel.getMediaCount() > 2000000000L ? 2000000000\n                                                           : Long.valueOf(hashtagModel.getMediaCount()).intValue(),\n                postCount)\n        );\n        span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);\n        span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);\n        hashtagDetailsBinding.mainTagPostCount.setText(span);\n        hashtagDetailsBinding.mainTagPostCount.setVisibility(View.VISIBLE);\n        //        hashtagDetailsBinding.mainHashtagImage.setOnClickListener(v -> {\n        //            if (!hasStories) return;\n        //            // show stories\n        //            final NavDirections action = HashTagFragmentDirections\n        //                    .actionHashtagFragmentToStoryViewerFragment(StoryViewerOptions.forHashtag(hashtagModel.getName()));\n        //            NavHostFragment.findNavController(this).navigate(action);\n        //        });\n    }\n\n    private void showSnackbar(final String message) {\n        @SuppressLint(\"ShowToast\") final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG);\n        snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss())\n                .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)\n                .setAnchorView(fragmentActivity.getBottomNavView())\n                .show();\n    }\n\n    private void setTitle() {\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        if (actionBar != null) {\n            actionBar.setTitle('#' + hashtag);\n        }\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() ->\n                                                              binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())\n        );\n    }\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = HashTagFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                Constants.PREF_HASHTAG_POSTS_LAYOUT,\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);\n                });\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.DividerItemDecoration;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment;\n\nimport java.util.List;\n\nimport awais.instagrabber.adapters.LikesAdapter;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoader;\nimport awais.instagrabber.databinding.FragmentLikesBinding;\nimport awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.MediaRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic final class LikesViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = LikesViewerFragment.class.getSimpleName();\n\n    private FragmentLikesBinding binding;\n    private RecyclerLazyLoader lazyLoader;\n    private MediaRepository mediaRepository;\n    private GraphQLRepository graphQLRepository;\n    private boolean isLoggedIn;\n    private String postId, endCursor;\n    private boolean isComment;\n\n    private final ServiceCallback<List<User>> cb = new ServiceCallback<List<User>>() {\n        @Override\n        public void onSuccess(final List<User> result) {\n            final LikesAdapter likesAdapter = new LikesAdapter(result, v -> {\n                final Object tag = v.getTag();\n                if (tag instanceof User) {\n                    User model = (User) tag;\n                    try {\n                        final NavDirections action = LikesViewerFragmentDirections.actionToProfile().setUsername(model.getUsername());\n                        NavHostFragment.findNavController(LikesViewerFragment.this).navigate(action);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"onSuccess: \", e);\n                    }\n                }\n            });\n            binding.rvLikes.setAdapter(likesAdapter);\n            final Context context = getContext();\n            if (context == null) return;\n            binding.rvLikes.setLayoutManager(new LinearLayoutManager(context));\n            binding.rvLikes.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));\n            binding.swipeRefreshLayout.setRefreshing(false);\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            Log.e(TAG, \"Error\", t);\n            try {\n                final Context context = getContext();\n                Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n            } catch (Exception ignored) {}\n        }\n    };\n\n    private final ServiceCallback<GraphQLUserListFetchResponse> anonCb = new ServiceCallback<GraphQLUserListFetchResponse>() {\n        @Override\n        public void onSuccess(final GraphQLUserListFetchResponse result) {\n            endCursor = result.getNextMaxId();\n            final LikesAdapter likesAdapter = new LikesAdapter(result.getItems(), v -> {\n                final Object tag = v.getTag();\n                if (tag instanceof User) {\n                    User model = (User) tag;\n                    try {\n                        final NavDirections action = LikesViewerFragmentDirections.actionToProfile().setUsername(model.getUsername());\n                        NavHostFragment.findNavController(LikesViewerFragment.this).navigate(action);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"onSuccess: \", e);\n                    }\n                }\n            });\n            binding.rvLikes.setAdapter(likesAdapter);\n            binding.swipeRefreshLayout.setRefreshing(false);\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            Log.e(TAG, \"Error\", t);\n            try {\n                final Context context = getContext();\n                Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n            } catch (Exception ignored) {}\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final long userId = CookieUtils.getUserIdFromCookie(cookie);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && userId != 0;\n        // final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);\n        final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        if (csrfToken == null) return;\n        mediaRepository = isLoggedIn ? MediaRepository.Companion.getInstance() : null;\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n        // setHasOptionsMenu(true);\n    }\n\n    @NonNull\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = FragmentLikesBinding.inflate(getLayoutInflater());\n        binding.swipeRefreshLayout.setEnabled(false);\n        binding.swipeRefreshLayout.setNestedScrollingEnabled(false);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init();\n    }\n\n    @Override\n    public void onRefresh() {\n        if (isComment && !isLoggedIn) {\n            lazyLoader.resetState();\n            graphQLRepository.fetchCommentLikers(\n                    postId,\n                    null,\n                    CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            anonCb.onFailure(throwable);\n                            return;\n                        }\n                        anonCb.onSuccess(response);\n                    }), Dispatchers.getIO())\n            );\n        } else {\n            mediaRepository.fetchLikes(\n                    postId,\n                    isComment,\n                    CoroutineUtilsKt.getContinuation((users, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            cb.onFailure(throwable);\n                            return;\n                        }\n                        //noinspection unchecked\n                        cb.onSuccess((List<User>) users);\n                    }), Dispatchers.getIO())\n            );\n        }\n    }\n\n    private void init() {\n        if (getArguments() == null) return;\n        final LikesViewerFragmentArgs fragmentArgs = LikesViewerFragmentArgs.fromBundle(getArguments());\n        postId = fragmentArgs.getPostId();\n        isComment = fragmentArgs.getIsComment();\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        binding.swipeRefreshLayout.setRefreshing(true);\n        if (isComment && !isLoggedIn) {\n            final Context context = getContext();\n            if (context == null) return;\n            final LinearLayoutManager layoutManager = new LinearLayoutManager(context);\n            binding.rvLikes.setLayoutManager(layoutManager);\n            binding.rvLikes.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.HORIZONTAL));\n            lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {\n                if (!TextUtils.isEmpty(endCursor)) {\n                    graphQLRepository.fetchCommentLikers(\n                            postId,\n                            endCursor,\n                            CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                if (throwable != null) {\n                                    anonCb.onFailure(throwable);\n                                    return;\n                                }\n                                anonCb.onSuccess(response);\n                            }), Dispatchers.getIO())\n                    );\n                }\n                endCursor = null;\n            });\n            binding.rvLikes.addOnScrollListener(lazyLoader);\n        }\n        onRefresh();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/LocationFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.annotation.SuppressLint;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.android.material.snackbar.BaseTransientBottomBar;\nimport com.google.android.material.snackbar.Snackbar;\nimport com.google.common.collect.ImmutableList;\n\nimport java.time.LocalDateTime;\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.asyncs.LocationPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentLocationBinding;\nimport awais.instagrabber.databinding.LayoutLocationDetailsBinding;\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.db.repositories.FavoriteRepository;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.LocationService;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"LocationFragment\";\n\n    private MainActivity fragmentActivity;\n    private FragmentLocationBinding binding;\n    private CoordinatorLayout root;\n    private boolean shouldRefresh = true;\n    private boolean opening = false;\n    private long locationId;\n    private Location locationModel;\n    private ActionMode actionMode;\n    private GraphQLRepository graphQLRepository;\n    private LocationService locationService;\n    private boolean isLoggedIn;\n    private Set<Media> selectedFeedModels;\n    private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_LOCATION_POSTS_LAYOUT);\n    private LayoutLocationDetailsBinding locationDetailsBinding;\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.posts.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {\n        @Override\n        public void onDestroy(final ActionMode mode) {\n            binding.posts.endSelection();\n        }\n\n        @Override\n        public boolean onActionItemClicked(final ActionMode mode,\n                                           final MenuItem item) {\n            if (item.getItemId() == R.id.action_download) {\n                if (LocationFragment.this.selectedFeedModels == null) return false;\n                final Context context = getContext();\n                if (context == null) return false;\n                DownloadUtils.download(context, ImmutableList.copyOf(LocationFragment.this.selectedFeedModels));\n                binding.posts.endSelection();\n                return true;\n            }\n            return false;\n        }\n    });\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            try {\n                final NavDirections commentsAction = LocationFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getPk(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(LocationFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = LocationFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(LocationFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media feedModel) {\n            final Location location = feedModel.getLocation();\n            if (location == null) return;\n            final NavDirections action = LocationFragmentDirections.actionToLocation(location.getPk());\n            NavHostFragment.findNavController(LocationFragment.this).navigate(action);\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(@NonNull final Media feedModel, final int position) {\n            if (opening) return;\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            if (TextUtils.isEmpty(user.getUsername())) {\n                opening = true;\n                final String code = feedModel.getCode();\n                if (code == null) return;\n                graphQLRepository.fetchPost(\n                        code,\n                        CoroutineUtilsKt.getContinuation((media, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                            opening = false;\n                            if (throwable != null) {\n                                Log.e(TAG, \"Error\", throwable);\n                                return;\n                            }\n                            if (media == null) return;\n                            openPostDialog(media, position);\n                        }))\n                );\n                return;\n            }\n            opening = true;\n            try {\n                final NavDirections action = LocationFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(LocationFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n            opening = false;\n        }\n    };\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            LocationFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n    private final ServiceCallback<Location> cb = new ServiceCallback<Location>() {\n        @Override\n        public void onSuccess(final Location result) {\n            locationModel = result;\n            binding.swipeRefreshLayout.setRefreshing(false);\n            setupLocationDetails();\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            setupLocationDetails();\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        locationService = isLoggedIn ? LocationService.getInstance() : null;\n        // storiesRepository = StoriesRepository.Companion.getInstance();\n        graphQLRepository = isLoggedIn ? null : GraphQLRepository.Companion.getInstance();\n        setHasOptionsMenu(true);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             @Nullable final ViewGroup container,\n                             @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentLocationBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        locationDetailsBinding = binding.header;\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.posts.refresh();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        fragmentActivity.setToolbar(binding.toolbar, this);\n        setTitle();\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.topic_posts_menu, menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        fragmentActivity.resetToolbar(this);\n    }\n\n    private void init() {\n        if (getArguments() == null) return;\n        final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments());\n        locationId = fragmentArgs.getLocationId();\n        locationDetailsBinding.favChip.setVisibility(View.GONE);\n        locationDetailsBinding.btnMap.setVisibility(View.GONE);\n        setTitle();\n        fetchLocationModel();\n    }\n\n    private void setupPosts() {\n        binding.posts.setViewModelStoreOwner(this)\n                     .setLifeCycleOwner(this)\n                     .setPostFetchService(new LocationPostFetchService(locationModel, isLoggedIn))\n                     .setLayoutPreferences(layoutPreferences)\n                     .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                     .setFeedItemCallback(feedItemCallback)\n                     .setSelectionModeCallback(selectionModeCallback)\n                     .init();\n        // binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() {\n        //     @Override\n        //     public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {\n        //         super.onScrolled(recyclerView, dx, dy);\n        //         final boolean canScrollVertically = recyclerView.canScrollVertically(-1);\n        //         final MotionScene.Transition transition = root.getTransition(R.id.transition);\n        //         if (transition != null) {\n        //             transition.setEnable(!canScrollVertically);\n        //         }\n        //     }\n        // });\n    }\n\n    private void fetchLocationModel() {\n        binding.swipeRefreshLayout.setRefreshing(true);\n        if (isLoggedIn) locationService.fetch(locationId, cb);\n        else graphQLRepository.fetchLocation(\n                locationId,\n                CoroutineUtilsKt.getContinuation((location, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        cb.onFailure(throwable);\n                        return;\n                    }\n                    cb.onSuccess(location);\n                }))\n        );\n    }\n\n    private void setupLocationDetails() {\n        if (locationModel == null) {\n            try {\n                Toast.makeText(getContext(), R.string.error_loading_location, Toast.LENGTH_SHORT).show();\n                binding.swipeRefreshLayout.setEnabled(false);\n            } catch (Exception ignored) {}\n            return;\n        }\n        setTitle();\n        setupPosts();\n        //        fetchStories();\n        final long locationId = locationModel.getPk();\n        // binding.swipeRefreshLayout.setRefreshing(true);\n        locationDetailsBinding.mainLocationImage.setImageURI(\"res:/\" + R.drawable.ic_location);\n        // final String postCount = String.valueOf(locationModel.getChildCommentCount());\n        // final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,\n        //                                                                                                 locationModel.getPostCount() > 2000000000L\n        //                                                                                                 ? 2000000000\n        //                                                                                                 : locationModel.getPostCount().intValue(),\n        //                                                                                                 postCount));\n        // span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);\n        // span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);\n        // locationDetailsBinding.mainLocPostCount.setText(span);\n        // locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE);\n        locationDetailsBinding.locationFullName.setText(locationModel.getName());\n        CharSequence biography = locationModel.getAddress() + \"\\n\" + locationModel.getCity();\n        // binding.locationBiography.setCaptionIsExpandable(true);\n        // binding.locationBiography.setCaptionIsExpanded(true);\n\n        final Context context = getContext();\n        if (context == null) return;\n        if (TextUtils.isEmpty(biography)) {\n            locationDetailsBinding.locationBiography.setVisibility(View.GONE);\n        } else {\n            locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE);\n            locationDetailsBinding.locationBiography.setText(biography);\n            // locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> {\n            //     final NavController navController = NavHostFragment.findNavController(this);\n            //     final Bundle bundle = new Bundle();\n            //     final String originalText = autoLinkItem.getOriginalText().trim();\n            //     bundle.putString(ARG_HASHTAG, originalText);\n            //     navController.navigate(R.id.action_global_hashTagFragment, bundle);\n            // });\n            // locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> {\n            //     final String originalText = autoLinkItem.getOriginalText().trim();\n            //     navigateToProfile(originalText);\n            // });\n            // locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,\n            //                                                                                                         autoLinkItem.getOriginalText()\n            //                                                                                                                     .trim()));\n            // locationDetailsBinding.locationBiography\n            //         .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));\n            locationDetailsBinding.locationBiography.setOnLongClickListener(v -> {\n                Utils.copyText(context, biography);\n                return true;\n            });\n        }\n\n        if (!locationModel.getGeo().startsWith(\"geo:0.0,0.0?z=17\")) {\n            locationDetailsBinding.btnMap.setVisibility(View.VISIBLE);\n            locationDetailsBinding.btnMap.setOnClickListener(v -> {\n                try {\n                    final Intent intent = new Intent(Intent.ACTION_VIEW);\n                    intent.setData(Uri.parse(locationModel.getGeo()));\n                    startActivity(intent);\n                } catch (ActivityNotFoundException e) {\n                    Toast.makeText(context, R.string.no_external_map_app, Toast.LENGTH_LONG).show();\n                    Log.e(TAG, \"setupLocationDetails: \", e);\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupLocationDetails: \", e);\n                }\n            });\n        } else {\n            locationDetailsBinding.btnMap.setVisibility(View.GONE);\n            locationDetailsBinding.btnMap.setOnClickListener(null);\n        }\n\n        final FavoriteRepository favoriteRepository = FavoriteRepository.Companion.getInstance(context);\n        locationDetailsBinding.favChip.setVisibility(View.VISIBLE);\n        favoriteRepository.getFavorite(\n                String.valueOf(locationId),\n                FavoriteType.LOCATION,\n                CoroutineUtilsKt.getContinuation((favorite, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null || favorite == null) {\n                        locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);\n                        locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);\n                        locationDetailsBinding.favChip.setText(R.string.add_to_favorites);\n                        Log.e(TAG, \"setupLocationDetails: \", throwable);\n                        return;\n                    }\n                    locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);\n                    locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);\n                    locationDetailsBinding.favChip.setText(R.string.favorite_short);\n                    favoriteRepository.insertOrUpdateFavorite(\n                            new Favorite(\n                                    favorite.getId(),\n                                    String.valueOf(locationId),\n                                    FavoriteType.LOCATION,\n                                    locationModel.getName(),\n                                    \"res:/\" + R.drawable.ic_location,\n                                    favorite.getDateAdded()\n                            ),\n                            CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                if (throwable1 != null) {\n                                    Log.e(TAG, \"onSuccess: \", throwable1);\n                                }\n                            }), Dispatchers.getIO())\n                    );\n                }), Dispatchers.getIO())\n        );\n        locationDetailsBinding.favChip.setOnClickListener(v -> favoriteRepository.getFavorite(\n                String.valueOf(locationId),\n                FavoriteType.LOCATION,\n                CoroutineUtilsKt.getContinuation((favorite, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"setupLocationDetails: \", throwable);\n                        return;\n                    }\n                    if (favorite == null) {\n                        favoriteRepository.insertOrUpdateFavorite(\n                                new Favorite(\n                                        0,\n                                        String.valueOf(locationId),\n                                        FavoriteType.LOCATION,\n                                        locationModel.getName(),\n                                        \"res:/\" + R.drawable.ic_location,\n                                        LocalDateTime.now()\n                                ),\n                                CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                    if (throwable1 != null) {\n                                        Log.e(TAG, \"onDataNotAvailable: \", throwable1);\n                                        return;\n                                    }\n                                    locationDetailsBinding.favChip.setText(R.string.favorite_short);\n                                    locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24);\n                                    showSnackbar(getString(R.string.added_to_favs));\n                                }), Dispatchers.getIO())\n                        );\n                        return;\n                    }\n                    favoriteRepository.deleteFavorite(\n                            String.valueOf(locationId),\n                            FavoriteType.LOCATION,\n                            CoroutineUtilsKt.getContinuation((unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                if (throwable1 != null) {\n                                    Log.e(TAG, \"onSuccess: \", throwable1);\n                                    return;\n                                }\n                                locationDetailsBinding.favChip.setText(R.string.add_to_favorites);\n                                locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24);\n                                showSnackbar(getString(R.string.removed_from_favs));\n                            }), Dispatchers.getIO())\n                    );\n                }), Dispatchers.getIO())\n        ));\n    }\n\n    private void showSnackbar(final String message) {\n        @SuppressLint(\"ShowToast\") final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG);\n        snackbar.setAction(R.string.ok, v1 -> snackbar.dismiss())\n                .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)\n                .setAnchorView(fragmentActivity.getBottomNavView())\n                .show();\n    }\n\n    private void setTitle() {\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        if (actionBar != null && locationModel != null) {\n            actionBar.setTitle(locationModel.getName());\n        }\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() -> binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()));\n    }\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = LocationFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                Constants.PREF_LOCATION_POSTS_LAYOUT,\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);\n                });\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.style.RelativeSizeSpan;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.app.NotificationManagerCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.NotificationsAdapter;\nimport awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;\nimport awais.instagrabber.databinding.FragmentNotificationsViewerBinding;\nimport awais.instagrabber.models.enums.NotificationType;\nimport awais.instagrabber.repositories.requests.StoryViewerOptions;\nimport awais.instagrabber.repositories.responses.notification.Notification;\nimport awais.instagrabber.repositories.responses.notification.NotificationArgs;\nimport awais.instagrabber.repositories.responses.notification.NotificationImage;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.NotificationViewModel;\nimport awais.instagrabber.webservices.FriendshipRepository;\nimport awais.instagrabber.webservices.MediaRepository;\nimport awais.instagrabber.webservices.NewsService;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlinx.coroutines.Dispatchers;\n\npublic final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"NotificationsViewer\";\n\n    private AppCompatActivity fragmentActivity;\n    private FragmentNotificationsViewerBinding binding;\n    private SwipeRefreshLayout root;\n    private boolean shouldRefresh = true;\n    private NotificationViewModel notificationViewModel;\n    private FriendshipRepository friendshipRepository;\n    private MediaRepository mediaRepository;\n    private NewsService newsService;\n    private String csrfToken, deviceUuid;\n    private String type;\n    private long targetId;\n    private Context context;\n    private long userId;\n\n    private final ServiceCallback<List<Notification>> cb = new ServiceCallback<List<Notification>>() {\n        @Override\n        public void onSuccess(final List<Notification> notificationModels) {\n            binding.swipeRefreshLayout.setRefreshing(false);\n            notificationViewModel.getList().postValue(notificationModels);\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            try {\n                binding.swipeRefreshLayout.setRefreshing(false);\n                Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();\n            } catch (Throwable ignored) {}\n        }\n    };\n\n    private final OnNotificationClickListener clickListener = new OnNotificationClickListener() {\n        @Override\n        public void onProfileClick(final String username) {\n            openProfile(username);\n        }\n\n        @Override\n        public void onPreviewClick(final Notification model) {\n            final NotificationImage notificationImage = model.getArgs().getMedia().get(0);\n            final long mediaId = Long.parseLong(notificationImage.getId().split(\"_\")[0]);\n            if (model.getType() == NotificationType.RESPONDED_STORY) {\n                final StoryViewerOptions options = StoryViewerOptions.forStory(\n                        mediaId,\n                        model.getArgs().getUsername()\n                );\n                try {\n                    final NavDirections action = NotificationsViewerFragmentDirections.actionToStory(options);\n                    NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);\n                } catch (Exception e) {\n                    Log.e(TAG, \"onPreviewClick: \", e);\n                }\n            } else {\n                final AlertDialog alertDialog = new AlertDialog.Builder(context)\n                        .setCancelable(false)\n                        .setView(R.layout.dialog_opening_post)\n                        .create();\n                alertDialog.show();\n                mediaRepository.fetch(\n                        mediaId,\n                        CoroutineUtilsKt.getContinuation((media, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                            if (throwable != null) {\n                                alertDialog.dismiss();\n                                Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();\n                                return;\n                            }\n                            try {\n                                final NavDirections action = NotificationsViewerFragmentDirections.actionToPost(media, 0);\n                                NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);\n                            } catch (Exception e) {\n                                Log.e(TAG, \"onSuccess: \", e);\n                            } finally {\n                                alertDialog.dismiss();\n                            }\n                        }), Dispatchers.getIO())\n                );\n            }\n        }\n\n        @Override\n        public void onNotificationClick(final Notification model) {\n            if (model == null) return;\n            final NotificationArgs args = model.getArgs();\n            final String username = args.getUsername();\n            if (model.getType() == NotificationType.FOLLOW || model.getType() == NotificationType.AYML) {\n                openProfile(username);\n            } else {\n                final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(args.getText()) ? \"\" : (\":\\n\" + args.getText())));\n                title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);\n\n                String[] commentDialogList;\n                if (model.getType() == NotificationType.RESPONDED_STORY) {\n                    commentDialogList = new String[]{\n                            getString(R.string.open_profile),\n                            getString(R.string.view_story)\n                    };\n                } else if (args.getMedia() != null) {\n                    commentDialogList = new String[]{\n                            getString(R.string.open_profile),\n                            getString(R.string.view_post)\n                    };\n                } else if (model.getType() == NotificationType.REQUEST) {\n                    commentDialogList = new String[]{\n                            getString(R.string.open_profile),\n                            getString(R.string.request_approve),\n                            getString(R.string.request_reject)\n                    };\n                } else commentDialogList = null; // shouldn't happen\n                final Context context = getContext();\n                if (context == null) return;\n                final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {\n                    switch (which) {\n                        case 0:\n                            openProfile(username);\n                            break;\n                        case 1:\n                            if (model.getType() == NotificationType.REQUEST) {\n                                friendshipRepository.approve(\n                                        csrfToken,\n                                        userId,\n                                        deviceUuid,\n                                        args.getUserId(),\n                                        CoroutineUtilsKt.getContinuation(\n                                                (response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                                    if (throwable != null) {\n                                                        Log.e(TAG, \"approve: onFailure: \", throwable);\n                                                        return;\n                                                    }\n                                                    onRefresh();\n                                                }),\n                                                Dispatchers.getIO()\n                                        )\n                                );\n                                return;\n                            }\n                            clickListener.onPreviewClick(model);\n                            break;\n                        case 2:\n                            friendshipRepository.ignore(\n                                    csrfToken,\n                                    userId,\n                                    deviceUuid,\n                                    args.getUserId(),\n                                    CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                        if (throwable != null) {\n                                            Log.e(TAG, \"approve: onFailure: \", throwable);\n                                            return;\n                                        }\n                                        onRefresh();\n                                    }), Dispatchers.getIO())\n                            );\n                            break;\n                    }\n                };\n                new AlertDialog.Builder(context)\n                        .setTitle(title)\n                        .setItems(commentDialogList, profileDialogListener)\n                        .setNegativeButton(R.string.cancel, null)\n                        .show();\n            }\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (AppCompatActivity) requireActivity();\n        context = getContext();\n        if (context == null) return;\n        NotificationManagerCompat.from(context.getApplicationContext()).cancel(Constants.ACTIVITY_NOTIFICATION_ID);\n        final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);\n        if (TextUtils.isEmpty(cookie)) {\n            Toast.makeText(context, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();\n        }\n        userId = CookieUtils.getUserIdFromCookie(cookie);\n        deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);\n        csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        friendshipRepository = FriendshipRepository.Companion.getInstance();\n        mediaRepository = MediaRepository.Companion.getInstance();\n        newsService = NewsService.getInstance();\n    }\n\n    @NonNull\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentNotificationsViewerBinding.inflate(getLayoutInflater());\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        init();\n        shouldRefresh = false;\n    }\n\n    private void init() {\n        final NotificationsViewerFragmentArgs fragmentArgs = NotificationsViewerFragmentArgs.fromBundle(getArguments());\n        type = fragmentArgs.getType();\n        targetId = fragmentArgs.getTargetId();\n        final Context context = getContext();\n        CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE));\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        notificationViewModel = new ViewModelProvider(this).get(NotificationViewModel.class);\n        final NotificationsAdapter adapter = new NotificationsAdapter(clickListener);\n        binding.rvComments.setLayoutManager(new LinearLayoutManager(context));\n        binding.rvComments.setAdapter(adapter);\n        notificationViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);\n        onRefresh();\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.swipeRefreshLayout.setRefreshing(true);\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        switch (type) {\n            case \"notif\":\n                if (actionBar != null) actionBar.setTitle(R.string.action_notif);\n                newsService.fetchAppInbox(true, cb);\n                break;\n            case \"ayml\":\n                if (actionBar != null) actionBar.setTitle(R.string.action_ayml);\n                newsService.fetchSuggestions(csrfToken, deviceUuid, cb);\n                break;\n            case \"chaining\":\n                if (actionBar != null) actionBar.setTitle(R.string.action_ayml);\n                newsService.fetchChaining(targetId, cb);\n                break;\n        }\n    }\n\n    private void openProfile(final String username) {\n        try {\n            final NavDirections action = NotificationsViewerFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"openProfile: \", e);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.graphics.Rect;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.appcompat.view.ContextThemeWrapper;\nimport androidx.appcompat.widget.PopupMenu;\nimport androidx.appcompat.widget.Toolbar;\nimport androidx.appcompat.widget.TooltipCompat;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.core.view.WindowCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.core.view.WindowInsetsControllerCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavBackStackEntry;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.transition.TransitionManager;\nimport androidx.viewpager2.widget.ViewPager2;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.drawee.drawable.ScalingUtils;\nimport com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;\nimport com.facebook.drawee.interfaces.DraweeController;\nimport com.facebook.imagepipeline.request.ImageRequest;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\nimport com.google.android.exoplayer2.ui.StyledPlayerView;\nimport com.google.android.material.appbar.CollapsingToolbarLayout;\nimport com.google.android.material.bottomnavigation.BottomNavigationView;\nimport com.google.android.material.snackbar.BaseTransientBottomBar;\nimport com.google.android.material.snackbar.Snackbar;\nimport com.skydoves.balloon.ArrowOrientation;\nimport com.skydoves.balloon.ArrowPositionRules;\nimport com.skydoves.balloon.Balloon;\nimport com.skydoves.balloon.BalloonAnimation;\nimport com.skydoves.balloon.BalloonHighlightAnimation;\nimport com.skydoves.balloon.BalloonSizeSpec;\nimport com.skydoves.balloon.overlay.BalloonOverlayAnimation;\nimport com.skydoves.balloon.overlay.BalloonOverlayCircle;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.SliderCallbackAdapter;\nimport awais.instagrabber.adapters.SliderItemsAdapter;\nimport awais.instagrabber.adapters.viewholder.SliderVideoViewHolder;\nimport awais.instagrabber.customviews.VerticalImageSpan;\nimport awais.instagrabber.customviews.VideoPlayerCallbackAdapter;\nimport awais.instagrabber.customviews.VideoPlayerViewHelper;\nimport awais.instagrabber.customviews.drawee.AnimatedZoomableController;\nimport awais.instagrabber.customviews.drawee.DoubleTapGestureListener;\nimport awais.instagrabber.customviews.drawee.ZoomableController;\nimport awais.instagrabber.customviews.drawee.ZoomableDraweeView;\nimport awais.instagrabber.databinding.DialogPostViewBinding;\nimport awais.instagrabber.databinding.LayoutPostViewBottomBinding;\nimport awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;\nimport awais.instagrabber.dialogs.EditTextDialogFragment;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Caption;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.MediaCandidate;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.NullSafePair;\nimport awais.instagrabber.utils.NumberUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.PostViewV2ViewModel;\n\nimport static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_SHOWN_COUNT_TOOLTIP;\n\npublic class PostViewV2Fragment extends Fragment implements EditTextDialogFragment.EditTextDialogFragmentCallback {\n    private static final String TAG = \"PostViewV2Fragment\";\n    // private static final int DETAILS_HIDE_DELAY_MILLIS = 2000;\n    public static final String ARG_MEDIA = \"media\";\n    public static final String ARG_SLIDER_POSITION = \"position\";\n\n    private DialogPostViewBinding binding;\n    private Context context;\n    private boolean detailsVisible = true;\n//    private boolean video;\n    private VideoPlayerViewHelper videoPlayerViewHelper;\n    private SliderItemsAdapter sliderItemsAdapter;\n    private int sliderPosition = -1;\n    private PostViewV2ViewModel viewModel;\n    private PopupMenu optionsPopup;\n    private EditTextDialogFragment editTextDialogFragment;\n    private boolean wasDeleted;\n    private MutableLiveData<Object> backStackSavedStateCollectionLiveData;\n    private MutableLiveData<Object> backStackSavedStateResultLiveData;\n    private OnDeleteListener onDeleteListener;\n    @Nullable\n    private ViewPager2 sliderParent;\n    private LayoutPostViewBottomBinding bottom;\n    private View postView;\n    private int originalHeight;\n    private boolean isInFullScreenMode;\n    private StyledPlayerView playerView;\n    private int playerViewOriginalHeight;\n    private Drawable originalRootBackground;\n    private ColorStateList originalLikeColorStateList;\n    private ColorStateList originalSaveColorStateList;\n    private WindowInsetsControllerCompat controller;\n\n    private final Observer<Object> backStackSavedStateObserver = result -> {\n        if (result == null) return;\n        if (result instanceof String) {\n            final String collection = (String) result;\n            handleSaveUnsaveResourceLiveData(viewModel.toggleSave(collection, viewModel.getMedia().getHasViewerSaved()));\n        } else if ((result instanceof RankedRecipient)) {\n            // Log.d(TAG, \"result: \" + result);\n            final Context context = getContext();\n            if (context != null) {\n                Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show();\n            }\n            viewModel.shareDm((RankedRecipient) result, sliderPosition);\n        } else if ((result instanceof Set)) {\n            try {\n                // Log.d(TAG, \"result: \" + result);\n                final Context context = getContext();\n                if (context != null) {\n                    Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show();\n                }\n                //noinspection unchecked\n                viewModel.shareDm((Set<RankedRecipient>) result, sliderPosition);\n            } catch (Exception e) {\n                Log.e(TAG, \"share: \", e);\n            }\n        }\n        // clear result\n        backStackSavedStateCollectionLiveData.postValue(null);\n        backStackSavedStateResultLiveData.postValue(null);\n    };\n\n    public void setOnDeleteListener(final OnDeleteListener onDeleteListener) {\n        if (onDeleteListener == null) return;\n        this.onDeleteListener = onDeleteListener;\n    }\n\n    public interface OnDeleteListener {\n        void onDelete();\n    }\n\n    // default constructor for fragment manager\n    public PostViewV2Fragment() {}\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        viewModel = new ViewModelProvider(this).get(PostViewV2ViewModel.class);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             @Nullable final ViewGroup container,\n                             @Nullable final Bundle savedInstanceState) {\n        binding = DialogPostViewBinding.inflate(inflater, container, false);\n        bottom = LayoutPostViewBottomBinding.bind(binding.getRoot());\n        final MainActivity activity = (MainActivity) getActivity();\n        if (activity == null) return null;\n        controller = new WindowInsetsControllerCompat(activity.getWindow(), activity.getRootView());\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        // postponeEnterTransition();\n        init();\n    }\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        this.context = context;\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        // wasPaused = true;\n        if (Utils.settingsHelper.getBoolean(PreferenceKeys.PLAY_IN_BACKGROUND)) return;\n        final Media media = viewModel.getMedia();\n        if (media.getType() == null) return;\n        switch (media.getType()) {\n            case MEDIA_TYPE_VIDEO:\n                if (videoPlayerViewHelper != null) {\n                    videoPlayerViewHelper.pause();\n                }\n                return;\n            case MEDIA_TYPE_SLIDER:\n                if (sliderItemsAdapter != null) {\n                    pauseSliderPlayer();\n                }\n            default:\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        final NavController navController = NavHostFragment.findNavController(this);\n        final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();\n        if (backStackEntry != null) {\n            backStackSavedStateCollectionLiveData = backStackEntry.getSavedStateHandle().getLiveData(\"collection\");\n            backStackSavedStateCollectionLiveData.observe(getViewLifecycleOwner(), backStackSavedStateObserver);\n            backStackSavedStateResultLiveData = backStackEntry.getSavedStateHandle().getLiveData(\"result\");\n            backStackSavedStateResultLiveData.observe(getViewLifecycleOwner(), backStackSavedStateObserver);\n        }\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        showSystemUI();\n        final Media media = viewModel.getMedia();\n        if (media.getType() == null) return;\n        switch (media.getType()) {\n            case MEDIA_TYPE_VIDEO:\n                if (videoPlayerViewHelper != null) {\n                    videoPlayerViewHelper.releasePlayer();\n                }\n                return;\n            case MEDIA_TYPE_SLIDER:\n                if (sliderItemsAdapter != null) {\n                    releaseAllSliderPlayers();\n                }\n            default:\n        }\n    }\n\n    @Override\n    public void onSaveInstanceState(@NonNull final Bundle outState) {\n        super.onSaveInstanceState(outState);\n        final Media media = viewModel.getMedia();\n        if (media.getType() == MediaItemType.MEDIA_TYPE_SLIDER) {\n            outState.putInt(ARG_SLIDER_POSITION, sliderPosition);\n        }\n    }\n\n    @Override\n    public void onPrimaryNavigationFragmentChanged(final boolean isPrimaryNavigationFragment) {\n        if (!isPrimaryNavigationFragment) {\n            final Media media = viewModel.getMedia();\n            switch (media.getType()) {\n                case MEDIA_TYPE_VIDEO:\n                    if (videoPlayerViewHelper != null) {\n                        videoPlayerViewHelper.pause();\n                    }\n                    return;\n                case MEDIA_TYPE_SLIDER:\n                    if (sliderItemsAdapter != null) {\n                        pauseSliderPlayer();\n                    }\n                default:\n            }\n        }\n    }\n\n    private void init() {\n        final Bundle arguments = getArguments();\n        if (arguments == null) {\n            // dismiss();\n            return;\n        }\n        final Serializable feedModelSerializable = arguments.getSerializable(ARG_MEDIA);\n        if (feedModelSerializable == null) {\n            Log.e(TAG, \"onCreate: feedModelSerializable is null\");\n            // dismiss();\n            return;\n        }\n        if (!(feedModelSerializable instanceof Media)) {\n            // dismiss();\n            return;\n        }\n        final Media media = (Media) feedModelSerializable;\n        if (media.getType() == MediaItemType.MEDIA_TYPE_SLIDER && sliderPosition == -1) {\n            sliderPosition = arguments.getInt(ARG_SLIDER_POSITION, 0);\n        }\n        viewModel.setMedia(media);\n        // if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {\n        //     binding.getRoot().getBackground().mutate().setAlpha(0);\n        // }\n        // setProfilePicSharedElement();\n        // setupCaptionBottomSheet();\n        setupCommonActions();\n        setObservers();\n    }\n\n    private void setObservers() {\n        viewModel.getUser().observe(getViewLifecycleOwner(), user -> {\n            if (user == null) {\n                binding.profilePic.setVisibility(View.GONE);\n                binding.title.setVisibility(View.GONE);\n                binding.subtitle.setVisibility(View.GONE);\n                return;\n            }\n            binding.profilePic.setVisibility(View.VISIBLE);\n            binding.title.setVisibility(View.VISIBLE);\n            binding.subtitle.setVisibility(View.VISIBLE);\n            binding.getRoot().post(() -> setupProfilePic(user));\n            binding.getRoot().post(() -> setupTitles(user));\n        });\n        viewModel.getCaption().observe(getViewLifecycleOwner(), caption -> binding.getRoot().post(() -> setupCaption(caption)));\n        viewModel.getLocation().observe(getViewLifecycleOwner(), location -> binding.getRoot().post(() -> setupLocation(location)));\n        viewModel.getDate().observe(getViewLifecycleOwner(), date -> binding.getRoot().post(() -> {\n            if (date == null) {\n                bottom.date.setVisibility(View.GONE);\n                return;\n            }\n            bottom.date.setVisibility(View.VISIBLE);\n            bottom.date.setText(date);\n        }));\n        viewModel.getLikeCount().observe(getViewLifecycleOwner(), count -> {\n            bottom.likesCount.setNumber(getSafeCount(count));\n            binding.getRoot().postDelayed(() -> bottom.likesCount.setAnimateChanges(true), 1000);\n            if (count > 1000 && !Utils.settingsHelper.getBoolean(PREF_SHOWN_COUNT_TOOLTIP)) {\n                binding.getRoot().postDelayed(this::showCountTooltip, 1000);\n            }\n        });\n        if (!viewModel.getMedia().getCommentsDisabled()) {\n            viewModel.getCommentCount().observe(getViewLifecycleOwner(), count -> {\n                bottom.commentsCount.setNumber(getSafeCount(count));\n                binding.getRoot().postDelayed(() -> bottom.commentsCount.setAnimateChanges(true), 1000);\n            });\n        }\n        viewModel.getViewCount().observe(getViewLifecycleOwner(), count -> {\n            if (count == null) {\n                bottom.viewsCount.setVisibility(View.GONE);\n                return;\n            }\n            bottom.viewsCount.setVisibility(View.VISIBLE);\n            final long safeCount = getSafeCount(count);\n            final String viewString = getResources().getQuantityString(R.plurals.views_count, (int) safeCount, safeCount);\n            bottom.viewsCount.setText(viewString);\n        });\n        viewModel.getType().observe(getViewLifecycleOwner(), this::setupPostTypeLayout);\n        viewModel.getLiked().observe(getViewLifecycleOwner(), this::setLikedResources);\n        viewModel.getSaved().observe(getViewLifecycleOwner(), this::setSavedResources);\n        viewModel.getOptions().observe(getViewLifecycleOwner(), options -> binding.getRoot().post(() -> {\n            setupOptions(options != null && !options.isEmpty());\n            createOptionsPopupMenu();\n        }));\n    }\n\n    private void showCountTooltip() {\n        final Context context = getContext();\n        if (context == null) return;\n        final Rect rect = new Rect();\n        bottom.likesCount.getGlobalVisibleRect(rect);\n        final Balloon balloon = new Balloon.Builder(context)\n                .setArrowSize(8)\n                .setArrowOrientation(ArrowOrientation.TOP)\n                .setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR)\n                .setArrowPosition(0.5f)\n                .setWidth(BalloonSizeSpec.WRAP)\n                .setHeight(BalloonSizeSpec.WRAP)\n                .setPadding(4)\n                .setTextSize(16)\n                .setAlpha(0.9f)\n                .setBalloonAnimation(BalloonAnimation.ELASTIC)\n                .setBalloonHighlightAnimation(BalloonHighlightAnimation.HEARTBEAT, 0)\n                .setIsVisibleOverlay(true)\n                .setOverlayColorResource(R.color.black_a50)\n                .setOverlayShape(new BalloonOverlayCircle((float) Math.max(\n                        bottom.likesCount.getMeasuredWidth(),\n                        bottom.likesCount.getMeasuredHeight()\n                ) / 2f))\n                .setBalloonOverlayAnimation(BalloonOverlayAnimation.FADE)\n                .setLifecycleOwner(getViewLifecycleOwner())\n                .setTextResource(R.string.click_to_show_full)\n                .setDismissWhenTouchOutside(false)\n                .setDismissWhenOverlayClicked(false)\n                .build();\n        balloon.showAlignBottom(bottom.likesCount);\n        Utils.settingsHelper.putBoolean(PREF_SHOWN_COUNT_TOOLTIP, true);\n        balloon.setOnBalloonOutsideTouchListener((view, motionEvent) -> {\n            if (rect.contains((int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {\n                bottom.likesCount.setShowAbbreviation(false);\n            }\n            balloon.dismiss();\n        });\n    }\n\n    @NonNull\n    private Long getSafeCount(final Long count) {\n        Long safeCount = count;\n        if (count == null) {\n            safeCount = 0L;\n        }\n        return safeCount;\n    }\n\n    private void setupCommonActions() {\n        setupLike();\n        setupSave();\n        setupDownload();\n        setupComment();\n        setupShare();\n    }\n\n    private void setupComment() {\n        if (!viewModel.hasPk() || viewModel.getMedia().getCommentsDisabled()) {\n            bottom.comment.setVisibility(View.GONE);\n            // bottom.commentsCount.setVisibility(View.GONE);\n            return;\n        }\n        bottom.comment.setVisibility(View.VISIBLE);\n        bottom.comment.setOnClickListener(v -> {\n            final Media media = viewModel.getMedia();\n            final User user = media.getUser();\n            if (user == null) return;\n            final NavController navController = getNavController();\n            if (navController == null) return;\n            try {\n                final NavDirections action = PostViewV2FragmentDirections.actionToComments(media.getCode(), media.getPk(), user.getPk());\n                navController.navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"setupComment: \", e);\n            }\n        });\n        TooltipCompat.setTooltipText(bottom.comment, getString(R.string.comment));\n    }\n\n    private void setupDownload() {\n        bottom.download.setOnClickListener(v -> DownloadUtils.showDownloadDialog(context, viewModel.getMedia(), sliderPosition, bottom.download));\n        TooltipCompat.setTooltipText(bottom.download, getString(R.string.action_download));\n    }\n\n    private void setupLike() {\n        originalLikeColorStateList = bottom.like.getIconTint();\n        final boolean likableMedia = viewModel.hasPk() /*&& viewModel.getMedia().isCommentLikesEnabled()*/;\n        if (!likableMedia) {\n            bottom.like.setVisibility(View.GONE);\n            // bottom.likesCount.setVisibility(View.GONE);\n            return;\n        }\n        if (!viewModel.isLoggedIn()) {\n            bottom.like.setVisibility(View.GONE);\n            return;\n        }\n        bottom.like.setOnClickListener(v -> {\n            v.setEnabled(false);\n            handleLikeUnlikeResourceLiveData(viewModel.toggleLike());\n        });\n        bottom.like.setOnLongClickListener(v -> {\n            final NavController navController = getNavController();\n            if (navController != null && viewModel.isLoggedIn()) {\n                try {\n                    final NavDirections action = PostViewV2FragmentDirections.actionToLikes(viewModel.getMedia().getPk(), false);\n                    navController.navigate(action);\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupLike: \", e);\n                }\n                return true;\n            }\n            return true;\n        });\n    }\n\n    private void handleLikeUnlikeResourceLiveData(@NonNull final LiveData<Resource<Object>> resource) {\n        resource.observe(getViewLifecycleOwner(), value -> {\n            switch (value.status) {\n                case SUCCESS:\n                    bottom.like.setEnabled(true);\n                    break;\n                case ERROR:\n                    bottom.like.setEnabled(true);\n                    unsuccessfulLike();\n                    break;\n                case LOADING:\n                    bottom.like.setEnabled(false);\n                    break;\n            }\n        });\n\n    }\n\n    private void unsuccessfulLike() {\n        final int errorTextResId;\n        final Media media = viewModel.getMedia();\n        if (!media.getHasLiked()) {\n            Log.e(TAG, \"like unsuccessful!\");\n            errorTextResId = R.string.like_unsuccessful;\n        } else {\n            Log.e(TAG, \"unlike unsuccessful!\");\n            errorTextResId = R.string.unlike_unsuccessful;\n        }\n        final Snackbar snackbar = Snackbar.make(binding.getRoot(), errorTextResId, BaseTransientBottomBar.LENGTH_INDEFINITE);\n        snackbar.setAction(R.string.ok, null);\n        snackbar.show();\n    }\n\n    private void setLikedResources(final boolean liked) {\n        final int iconResource;\n        final ColorStateList tintColorStateList;\n        final Context context = getContext();\n        if (context == null) return;\n        final Resources resources = context.getResources();\n        if (resources == null) return;\n        if (liked) {\n            iconResource = R.drawable.ic_like;\n            tintColorStateList = ColorStateList.valueOf(resources.getColor(R.color.red_600));\n        } else {\n            iconResource = R.drawable.ic_not_liked;\n            tintColorStateList = originalLikeColorStateList != null ? originalLikeColorStateList\n                                                                    : ColorStateList.valueOf(resources.getColor(R.color.white));\n        }\n        bottom.like.setIconResource(iconResource);\n        bottom.like.setIconTint(tintColorStateList);\n    }\n\n    private void setupSave() {\n        originalSaveColorStateList = bottom.save.getIconTint();\n        if (!viewModel.isLoggedIn() || !viewModel.hasPk() || !viewModel.getMedia().getCanViewerSave()) {\n            bottom.save.setVisibility(View.GONE);\n            return;\n        }\n        bottom.save.setOnClickListener(v -> {\n            bottom.save.setEnabled(false);\n            handleSaveUnsaveResourceLiveData(viewModel.toggleSave());\n        });\n        bottom.save.setOnLongClickListener(v -> {\n            try {\n                final NavDirections action = PostViewV2FragmentDirections.actionToSavedCollections().setIsSaving(true);\n                NavHostFragment.findNavController(this).navigate(action);\n                return true;\n            } catch (Exception e) {\n                Log.e(TAG, \"setupSave: \", e);\n            }\n            return false;\n        });\n    }\n\n    private void handleSaveUnsaveResourceLiveData(@NonNull final LiveData<Resource<Object>> resource) {\n        resource.observe(getViewLifecycleOwner(), value -> {\n            if (value == null) return;\n            switch (value.status) {\n                case SUCCESS:\n                    bottom.save.setEnabled(true);\n                    break;\n                case ERROR:\n                    bottom.save.setEnabled(true);\n                    unsuccessfulSave();\n                    break;\n                case LOADING:\n                    bottom.save.setEnabled(false);\n                    break;\n            }\n        });\n    }\n\n    private void unsuccessfulSave() {\n        final int errorTextResId;\n        final Media media = viewModel.getMedia();\n        if (!media.getHasViewerSaved()) {\n            Log.e(TAG, \"save unsuccessful!\");\n            errorTextResId = R.string.save_unsuccessful;\n        } else {\n            Log.e(TAG, \"save remove unsuccessful!\");\n            errorTextResId = R.string.save_remove_unsuccessful;\n        }\n        final Snackbar snackbar = Snackbar.make(binding.getRoot(), errorTextResId, Snackbar.LENGTH_INDEFINITE);\n        snackbar.setAction(R.string.ok, null);\n        snackbar.show();\n    }\n\n    private void setSavedResources(final boolean saved) {\n        final int iconResource;\n        final ColorStateList tintColorStateList;\n        final Context context = getContext();\n        if (context == null) return;\n        final Resources resources = context.getResources();\n        if (resources == null) return;\n        if (saved) {\n            iconResource = R.drawable.ic_bookmark;\n            tintColorStateList = ColorStateList.valueOf(resources.getColor(R.color.blue_700));\n        } else {\n            iconResource = R.drawable.ic_round_bookmark_border_24;\n            tintColorStateList = originalSaveColorStateList != null ? originalSaveColorStateList\n                                                                    : ColorStateList.valueOf(resources.getColor(R.color.white));\n        }\n        bottom.save.setIconResource(iconResource);\n        bottom.save.setIconTint(tintColorStateList);\n    }\n\n    private void setupProfilePic(final User user) {\n        if (user == null) {\n            binding.profilePic.setImageURI((String) null);\n            return;\n        }\n        final String uri = user.getProfilePicUrl();\n        final DraweeController controller = Fresco\n                .newDraweeControllerBuilder()\n                .setUri(uri)\n                .build();\n        binding.profilePic.setController(controller);\n        binding.profilePic.setOnClickListener(v -> navigateToProfile(\"@\" + user.getUsername()));\n    }\n\n    private void setupTitles(final User user) {\n        if (user == null) {\n            binding.title.setVisibility(View.GONE);\n            binding.subtitle.setVisibility(View.GONE);\n            return;\n        }\n        final String fullName = user.getFullName();\n        if (TextUtils.isEmpty(fullName)) {\n            binding.subtitle.setVisibility(View.GONE);\n        } else {\n            binding.subtitle.setVisibility(View.VISIBLE);\n            binding.subtitle.setText(fullName);\n        }\n        setUsername(user);\n        binding.title.setOnClickListener(v -> navigateToProfile(\"@\" + user.getUsername()));\n        binding.subtitle.setOnClickListener(v -> navigateToProfile(\"@\" + user.getUsername()));\n    }\n\n    private void setUsername(final User user) {\n        final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());\n        final int drawableSize = Utils.convertDpToPx(24);\n        if (user.isVerified()) {\n            final Context context = getContext();\n            if (context == null) return;\n            final Drawable verifiedDrawable = AppCompatResources.getDrawable(context, R.drawable.verified);\n            VerticalImageSpan verifiedSpan = null;\n            if (verifiedDrawable != null) {\n                final Drawable drawable = verifiedDrawable.mutate();\n                drawable.setBounds(0, 0, drawableSize, drawableSize);\n                verifiedSpan = new VerticalImageSpan(drawable);\n            }\n            try {\n                if (verifiedSpan != null) {\n                    sb.append(\"  \");\n                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"setUsername: \", e);\n            }\n        }\n        binding.title.setText(sb);\n    }\n\n    private void setupCaption(final Caption caption) {\n        if (caption == null || TextUtils.isEmpty(caption.getText())) {\n            bottom.caption.setVisibility(View.GONE);\n            bottom.translate.setVisibility(View.GONE);\n            return;\n        }\n        final String postCaption = caption.getText();\n        bottom.caption.addOnHashtagListener(autoLinkItem -> {\n            try {\n                final String originalText = autoLinkItem.getOriginalText().trim();\n                final NavDirections action = PostViewV2FragmentDirections.actionToHashtag(originalText);\n                NavHostFragment.findNavController(this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"setupCaption: \", e);\n            }\n        });\n        bottom.caption.addOnMentionClickListener(autoLinkItem -> {\n            final String originalText = autoLinkItem.getOriginalText().trim();\n            navigateToProfile(originalText);\n        });\n        bottom.caption.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(), autoLinkItem.getOriginalText().trim()));\n        bottom.caption.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));\n        bottom.caption.setOnLongClickListener(v -> {\n            final Context context = getContext();\n            if (context == null) return false;\n            Utils.copyText(context, postCaption);\n            return true;\n        });\n        bottom.caption.setText(postCaption);\n        bottom.translate.setOnClickListener(v -> handleTranslateCaptionResource(viewModel.translateCaption()));\n    }\n\n    private void handleTranslateCaptionResource(@NonNull final LiveData<Resource<String>> data) {\n        data.observe(getViewLifecycleOwner(), resource -> {\n            if (resource == null) return;\n            switch (resource.status) {\n                case SUCCESS:\n                    bottom.translate.setVisibility(View.GONE);\n                    bottom.caption.setText(resource.data);\n                    break;\n                case ERROR:\n                    bottom.translate.setEnabled(true);\n                    String message = resource.message;\n                    if (TextUtils.isEmpty(message)) {\n                        message = getString(R.string.downloader_unknown_error);\n                    }\n                    final Snackbar snackbar = Snackbar.make(binding.getRoot(), message, Snackbar.LENGTH_INDEFINITE);\n                    snackbar.setAction(R.string.ok, null);\n                    snackbar.show();\n                    break;\n                case LOADING:\n                    bottom.translate.setEnabled(false);\n                    break;\n            }\n        });\n    }\n\n    private void setupLocation(final Location location) {\n        if (location == null || !detailsVisible) {\n            binding.location.setVisibility(View.GONE);\n            return;\n        }\n        final String locationName = location.getName();\n        if (TextUtils.isEmpty(locationName)) return;\n        binding.location.setText(locationName);\n        binding.location.setVisibility(View.VISIBLE);\n        binding.location.setOnClickListener(v -> {\n            try {\n                final NavController navController = getNavController();\n                if (navController == null) return;\n                final NavDirections action = PostViewV2FragmentDirections.actionToLocation(location.getPk());\n                navController.navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"setupLocation: \", e);\n            }\n        });\n    }\n\n    private void setupShare() {\n        if (!viewModel.hasPk()) {\n            bottom.share.setVisibility(View.GONE);\n            return;\n        }\n        bottom.share.setVisibility(View.VISIBLE);\n        TooltipCompat.setTooltipText(bottom.share, getString(R.string.share));\n        bottom.share.setOnClickListener(v -> {\n            final Media media = viewModel.getMedia();\n            final User profileModel = media.getUser();\n            if (profileModel == null) return;\n            if (viewModel.isLoggedIn()) {\n                final Context context = getContext();\n                if (context == null) return;\n                final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle);\n                final PopupMenu popupMenu = new PopupMenu(themeWrapper, bottom.share);\n                final Menu menu = popupMenu.getMenu();\n                menu.add(0, R.id.share_dm, 0, R.string.share_via_dm);\n                menu.add(0, R.id.share, 1, R.string.share_link);\n                popupMenu.setOnMenuItemClickListener(item -> {\n                    final int itemId = item.getItemId();\n                    if (itemId == R.id.share_dm) {\n                        if (profileModel.isPrivate()) Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show();\n                        final PostViewV2FragmentDirections.ActionToUserSearch actionGlobalUserSearch = PostViewV2FragmentDirections\n                                .actionToUserSearch()\n                                .setTitle(getString(R.string.share))\n                                .setActionLabel(getString(R.string.send))\n                                .setShowGroups(true)\n                                .setMultiple(true)\n                                .setSearchMode(UserSearchMode.RAVEN);\n                        final NavController navController = NavHostFragment.findNavController(PostViewV2Fragment.this);\n                        try {\n                            navController.navigate(actionGlobalUserSearch);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"setupShare: \", e);\n                        }\n                        return true;\n                    } else if (itemId == R.id.share) {\n                        shareLink(media, profileModel.isPrivate());\n                        return true;\n                    }\n                    return false;\n                });\n                popupMenu.show();\n                return;\n            }\n            shareLink(media, false);\n        });\n    }\n\n    private void shareLink(@NonNull final Media media, final boolean isPrivate) {\n        final Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);\n        sharingIntent.setType(\"text/plain\");\n        sharingIntent.putExtra(android.content.Intent.EXTRA_TITLE,\n                               getString(isPrivate ? R.string.share_private_post : R.string.share_public_post));\n        sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, \"https://instagram.com/p/\" + media.getCode());\n        startActivity(Intent.createChooser(\n                sharingIntent,\n                isPrivate ? getString(R.string.share_private_post)\n                          : getString(R.string.share_public_post)\n        ));\n    }\n\n    private void setupPostTypeLayout(final MediaItemType type) {\n        if (type == null) return;\n        switch (type) {\n            case MEDIA_TYPE_IMAGE:\n                setupPostImage();\n                break;\n            case MEDIA_TYPE_SLIDER:\n                setupSlider();\n                break;\n            case MEDIA_TYPE_VIDEO:\n                setupVideo();\n                break;\n        }\n    }\n\n    private void setupPostImage() {\n        // binding.mediaCounter.setVisibility(View.GONE);\n        final Context context = getContext();\n        if (context == null) return;\n        final Resources resources = context.getResources();\n        if (resources == null) return;\n        final Media media = viewModel.getMedia();\n        final String imageUrl = ResponseBodyUtils.getImageUrl(media);\n        if (TextUtils.isEmpty(imageUrl)) return;\n        final ZoomableDraweeView postImage = new ZoomableDraweeView(context);\n        postView = postImage;\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(media.getOriginalHeight(),\n                                                                                            media.getOriginalWidth(),\n                                                                                            (int) (Utils.displayMetrics.heightPixels * 0.8),\n                                                                                            Utils.displayMetrics.widthPixels);\n        originalHeight = widthHeight.second;\n        final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT,\n                                                                                             originalHeight);\n        postImage.setLayoutParams(layoutParams);\n        postImage.setHierarchy(new GenericDraweeHierarchyBuilder(resources)\n                                       .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)\n                                       .build());\n\n        postImage.setController(Fresco.newDraweeControllerBuilder()\n                                      .setLowResImageRequest(ImageRequest.fromUri(ResponseBodyUtils.getThumbUrl(media)))\n                                      .setImageRequest(ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl))\n                                                                          .setLocalThumbnailPreviewsEnabled(true)\n                                                                          .build())\n                                      .build());\n        final AnimatedZoomableController zoomableController = (AnimatedZoomableController) postImage.getZoomableController();\n        zoomableController.setMaxScaleFactor(3f);\n        zoomableController.setGestureZoomEnabled(true);\n        zoomableController.setEnabled(true);\n        postImage.setZoomingEnabled(true);\n        final DoubleTapGestureListener tapListener = new DoubleTapGestureListener(postImage) {\n            @Override\n            public boolean onSingleTapConfirmed(final MotionEvent e) {\n                if (!isInFullScreenMode) {\n                    zoomableController.reset();\n                    hideSystemUI();\n                } else {\n                    showSystemUI();\n                    binding.getRoot().postDelayed(zoomableController::reset, 500);\n                }\n                return super.onSingleTapConfirmed(e);\n            }\n        };\n        postImage.setTapListener(tapListener);\n        binding.postContainer.addView(postView);\n    }\n\n    private void setupSlider() {\n        final Media media = viewModel.getMedia();\n        binding.mediaCounter.setVisibility(View.VISIBLE);\n        final Context context = getContext();\n        if (context == null) return;\n        sliderParent = new ViewPager2(context);\n        final List<Media> carouselMedia = media.getCarouselMedia();\n        if (carouselMedia == null) return;\n        final NullSafePair<Integer, Integer> maxHW = carouselMedia\n                .stream()\n                .reduce(new NullSafePair<>(0, 0),\n                        (prev, m) -> {\n                            final int height = m.getOriginalHeight() > prev.first ? m.getOriginalHeight() : prev.first;\n                            final int width = m.getOriginalWidth() > prev.second ? m.getOriginalWidth() : prev.second;\n                            return new NullSafePair<>(height, width);\n                        },\n                        (p1, p2) -> {\n                            final int height = p1.first > p2.first ? p1.first : p2.first;\n                            final int width = p1.second > p2.second ? p1.second : p2.second;\n                            return new NullSafePair<>(height, width);\n                        });\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(maxHW.first,\n                                                                                            maxHW.second,\n                                                                                            (int) (Utils.displayMetrics.heightPixels * 0.8),\n                                                                                            Utils.displayMetrics.widthPixels);\n        originalHeight = widthHeight.second;\n        final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT,\n                                                                                             originalHeight);\n        sliderParent.setLayoutParams(layoutParams);\n        postView = sliderParent;\n        // binding.contentRoot.addView(sliderParent, 0);\n        binding.postContainer.addView(postView);\n\n        final boolean hasVideo = media.getCarouselMedia()\n                                      .stream()\n                                      .anyMatch(postChild -> postChild.getType() == MediaItemType.MEDIA_TYPE_VIDEO);\n        if (hasVideo) {\n            final View child = sliderParent.getChildAt(0);\n            if (child instanceof RecyclerView) {\n                ((RecyclerView) child).setItemViewCacheSize(media.getCarouselMedia().size());\n                ((RecyclerView) child).addRecyclerListener(holder -> {\n                    if (holder instanceof SliderVideoViewHolder) {\n                        ((SliderVideoViewHolder) holder).releasePlayer();\n                    }\n                });\n            }\n        }\n        sliderItemsAdapter = new SliderItemsAdapter(true, new SliderCallbackAdapter() {\n            @Override\n            public void onItemClicked(final int position, final Media media, final View view) {\n                if (media == null\n                        || media.getType() != MediaItemType.MEDIA_TYPE_IMAGE\n                        || !(view instanceof ZoomableDraweeView)) {\n                    return;\n                }\n                final ZoomableController zoomableController = ((ZoomableDraweeView) view).getZoomableController();\n                if (!(zoomableController instanceof AnimatedZoomableController)) return;\n                if (!isInFullScreenMode) {\n                    ((AnimatedZoomableController) zoomableController).reset();\n                    hideSystemUI();\n                    return;\n                }\n                showSystemUI();\n                binding.getRoot().postDelayed(((AnimatedZoomableController) zoomableController)::reset, 500);\n            }\n\n            @Override\n            public void onPlayerPlay(final int position) {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.enabledKeepScreenOn(activity);\n                // if (!detailsVisible || hasBeenToggled) return;\n                // showPlayerControls();\n            }\n\n            @Override\n            public void onPlayerPause(final int position) {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.disableKeepScreenOn(activity);\n                // if (detailsVisible || hasBeenToggled) return;\n                // toggleDetails();\n            }\n\n            @Override\n            public void onPlayerRelease(final int position) {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.disableKeepScreenOn(activity);\n            }\n\n            @Override\n            public void onFullScreenModeChanged(final boolean isFullScreen, final StyledPlayerView playerView) {\n                PostViewV2Fragment.this.playerView = playerView;\n                if (isFullScreen) {\n                    hideSystemUI();\n                    return;\n                }\n                showSystemUI();\n            }\n\n            @Override\n            public boolean isInFullScreen() {\n                return isInFullScreenMode;\n            }\n        });\n        sliderParent.setAdapter(sliderItemsAdapter);\n        if (sliderPosition >= 0 && sliderPosition < media.getCarouselMedia().size()) {\n            sliderParent.setCurrentItem(sliderPosition);\n        }\n        sliderParent.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {\n            int prevPosition = -1;\n\n            @Override\n            public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {\n                if (prevPosition != -1) {\n                    final View view = sliderParent.getChildAt(0);\n                    if (view instanceof RecyclerView) {\n                        pausePlayerAtPosition(prevPosition, (RecyclerView) view);\n                        pausePlayerAtPosition(position, (RecyclerView) view);\n                    }\n                }\n                if (positionOffset == 0) {\n                    prevPosition = position;\n                }\n            }\n\n            @Override\n            public void onPageSelected(final int position) {\n                final int size = media.getCarouselMedia().size();\n                if (position < 0 || position >= size) return;\n                sliderPosition = position;\n                final String text = (position + 1) + \"/\" + size;\n                binding.mediaCounter.setText(text);\n                final Media childMedia = media.getCarouselMedia().get(position);\n//                video = false;\n//                if (childMedia.getType() == MediaItemType.MEDIA_TYPE_VIDEO) {\n//                    video = true;\n//                    viewModel.setViewCount(childMedia.getViewCount());\n//                    return;\n//                }\n//                viewModel.setViewCount(null);\n            }\n\n            private void pausePlayerAtPosition(final int position, final RecyclerView view) {\n                final RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position);\n                if (viewHolder instanceof SliderVideoViewHolder) {\n                    ((SliderVideoViewHolder) viewHolder).pause();\n                }\n            }\n        });\n        final String text = \"1/\" + carouselMedia.size();\n        binding.mediaCounter.setText(text);\n        sliderItemsAdapter.submitList(media.getCarouselMedia());\n        sliderParent.setCurrentItem(sliderPosition);\n    }\n\n    private void pauseSliderPlayer() {\n        if (sliderParent == null) return;\n        final int currentItem = sliderParent.getCurrentItem();\n        final View view = sliderParent.getChildAt(0);\n        if (!(view instanceof RecyclerView)) return;\n        final RecyclerView.ViewHolder viewHolder = ((RecyclerView) view).findViewHolderForAdapterPosition(currentItem);\n        if (!(viewHolder instanceof SliderVideoViewHolder)) return;\n        ((SliderVideoViewHolder) viewHolder).pause();\n    }\n\n    private void releaseAllSliderPlayers() {\n        if (sliderParent == null) return;\n        final View view = sliderParent.getChildAt(0);\n        if (!(view instanceof RecyclerView)) return;\n        final int itemCount = sliderItemsAdapter.getItemCount();\n        for (int position = itemCount - 1; position >= 0; position--) {\n            final RecyclerView.ViewHolder viewHolder = ((RecyclerView) view).findViewHolderForAdapterPosition(position);\n            if (!(viewHolder instanceof SliderVideoViewHolder)) continue;\n            ((SliderVideoViewHolder) viewHolder).releasePlayer();\n        }\n    }\n\n    private void setupVideo() {\n//        video = true;\n        final Media media = viewModel.getMedia();\n        binding.mediaCounter.setVisibility(View.GONE);\n        final Context context = getContext();\n        if (context == null) return;\n        final LayoutVideoPlayerWithThumbnailBinding videoPost = LayoutVideoPlayerWithThumbnailBinding\n                .inflate(LayoutInflater.from(context), binding.contentRoot, false);\n        final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) videoPost.getRoot().getLayoutParams();\n        final NullSafePair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(media.getOriginalHeight(),\n                                                                                            media.getOriginalWidth(),\n                                                                                            (int) (Utils.displayMetrics.heightPixels * 0.8),\n                                                                                            Utils.displayMetrics.widthPixels);\n        layoutParams.width = ConstraintLayout.LayoutParams.MATCH_PARENT;\n        originalHeight = widthHeight.second;\n        layoutParams.height = originalHeight;\n        postView = videoPost.getRoot();\n        binding.postContainer.addView(postView);\n\n        // final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {\n        //     @Override\n        //     public boolean onSingleTapConfirmed(final MotionEvent e) {\n        //         videoPost.playerView.performClick();\n        //         return true;\n        //     }\n        // });\n        // videoPost.playerView.setOnTouchListener((v, event) -> {\n        //     gestureDetector.onTouchEvent(event);\n        //     return true;\n        // });\n        final float vol = Utils.settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;\n        final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {\n            @Override\n            public void onThumbnailLoaded() {\n                startPostponedEnterTransition();\n            }\n\n            @Override\n            public void onPlayerViewLoaded() {\n                // binding.playerControls.getRoot().setVisibility(View.VISIBLE);\n                final ViewGroup.LayoutParams layoutParams = videoPost.playerView.getLayoutParams();\n                final int requiredWidth = Utils.displayMetrics.widthPixels;\n                final int resultingHeight = NumberUtils\n                        .getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());\n                layoutParams.width = requiredWidth;\n                layoutParams.height = resultingHeight;\n                videoPost.playerView.requestLayout();\n            }\n\n            @Override\n            public void onPlay() {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.enabledKeepScreenOn(activity);\n                // if (detailsVisible) {\n                //     new Handler().postDelayed(() -> toggleDetails(), DETAILS_HIDE_DELAY_MILLIS);\n                // }\n            }\n\n            @Override\n            public void onPause() {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.disableKeepScreenOn(activity);\n            }\n\n            @Override\n            public void onRelease() {\n                final FragmentActivity activity = getActivity();\n                if (activity == null) return;\n                Utils.disableKeepScreenOn(activity);\n            }\n\n            @Override\n            public void onFullScreenModeChanged(final boolean isFullScreen, final StyledPlayerView playerView) {\n                PostViewV2Fragment.this.playerView = playerView;\n                if (isFullScreen) {\n                    hideSystemUI();\n                    return;\n                }\n                showSystemUI();\n            }\n        };\n        final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();\n        String videoUrl = null;\n        final List<MediaCandidate> videoVersions = media.getVideoVersions();\n        if (videoVersions != null && !videoVersions.isEmpty()) {\n            final MediaCandidate videoVersion = videoVersions.get(0);\n            if (videoVersion != null) {\n                videoUrl = videoVersion.getUrl();\n            }\n        }\n        if (videoUrl != null) {\n            videoPlayerViewHelper = new VideoPlayerViewHelper(\n                    binding.getRoot().getContext(),\n                    videoPost,\n                    videoUrl,\n                    vol,\n                    aspectRatio,\n                    ResponseBodyUtils.getThumbUrl(media),\n                    true,\n                    videoPlayerCallback);\n        }\n    }\n\n    private void setupOptions(final Boolean show) {\n        if (!show) {\n            binding.options.setVisibility(View.GONE);\n            return;\n        }\n        binding.options.setVisibility(View.VISIBLE);\n        binding.options.setOnClickListener(v -> {\n            if (optionsPopup == null) return;\n            optionsPopup.show();\n        });\n    }\n\n    private void createOptionsPopupMenu() {\n        if (optionsPopup == null) {\n            final Context context = getContext();\n            if (context == null) return;\n            final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle);\n            optionsPopup = new PopupMenu(themeWrapper, binding.options);\n        } else {\n            optionsPopup.getMenu().clear();\n        }\n        optionsPopup.getMenuInflater().inflate(R.menu.post_view_menu, optionsPopup.getMenu());\n        // final Menu menu = optionsPopup.getMenu();\n        // final int size = menu.size();\n        // for (int i = 0; i < size; i++) {\n        //     final MenuItem item = menu.getItem(i);\n        //     if (item == null) continue;\n        //     if (options.contains(item.getItemId())) continue;\n        //     menu.removeItem(item.getItemId());\n        // }\n        optionsPopup.setOnMenuItemClickListener(item -> {\n            int itemId = item.getItemId();\n            if (itemId == R.id.edit_caption) {\n                showCaptionEditDialog();\n                return true;\n            }\n            if (itemId == R.id.delete) {\n                item.setEnabled(false);\n                final LiveData<Resource<Object>> resourceLiveData = viewModel.delete();\n                handleDeleteResource(resourceLiveData, item);\n            }\n            return true;\n        });\n    }\n\n    private void handleDeleteResource(final LiveData<Resource<Object>> resourceLiveData, final MenuItem item) {\n        if (resourceLiveData == null) return;\n        resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {\n            @Override\n            public void onChanged(final Resource<Object> resource) {\n                try {\n                    switch (resource.status) {\n                        case SUCCESS:\n                            wasDeleted = true;\n                            if (onDeleteListener != null) {\n                                onDeleteListener.onDelete();\n                            }\n                            break;\n                        case ERROR:\n                            if (item != null) {\n                                item.setEnabled(true);\n                            }\n                            final Snackbar snackbar = Snackbar.make(binding.getRoot(),\n                                                                    R.string.delete_unsuccessful,\n                                                                    Snackbar.LENGTH_INDEFINITE);\n                            snackbar.setAction(R.string.ok, null);\n                            snackbar.show();\n                            break;\n                        case LOADING:\n                            if (item != null) {\n                                item.setEnabled(false);\n                            }\n                            break;\n                    }\n                } finally {\n                    resourceLiveData.removeObserver(this);\n                }\n            }\n        });\n    }\n\n    private void showCaptionEditDialog() {\n        final Caption caption = viewModel.getCaption().getValue();\n        final String captionText = caption != null ? caption.getText() : null;\n        editTextDialogFragment = EditTextDialogFragment\n                .newInstance(R.string.edit_caption, R.string.confirm, R.string.cancel, captionText);\n        editTextDialogFragment.show(getChildFragmentManager(), \"edit_caption\");\n    }\n\n    @Override\n    public void onPositiveButtonClicked(final String caption) {\n        handleEditCaptionResource(viewModel.updateCaption(caption));\n        if (editTextDialogFragment == null) return;\n        editTextDialogFragment.dismiss();\n        editTextDialogFragment = null;\n    }\n\n    private void handleEditCaptionResource(final LiveData<Resource<Object>> updateCaption) {\n        if (updateCaption == null) return;\n        updateCaption.observe(getViewLifecycleOwner(), resource -> {\n            final MenuItem item = optionsPopup.getMenu().findItem(R.id.edit_caption);\n            switch (resource.status) {\n                case SUCCESS:\n                    if (item != null) {\n                        item.setEnabled(true);\n                    }\n                    break;\n                case ERROR:\n                    if (item != null) {\n                        item.setEnabled(true);\n                    }\n                    final Snackbar snackbar = Snackbar.make(binding.getRoot(), R.string.edit_unsuccessful, BaseTransientBottomBar.LENGTH_INDEFINITE);\n                    snackbar.setAction(R.string.ok, null);\n                    snackbar.show();\n                    break;\n                case LOADING:\n                    if (item != null) {\n                        item.setEnabled(false);\n                    }\n                    break;\n            }\n        });\n    }\n\n    @Override\n    public void onNegativeButtonClicked() {\n        if (editTextDialogFragment == null) return;\n        editTextDialogFragment.dismiss();\n        editTextDialogFragment = null;\n    }\n\n    private void toggleDetails() {\n        // final boolean hasBeenToggled = true;\n        final MainActivity activity = (MainActivity) getActivity();\n        if (activity == null) return;\n        final Media media = viewModel.getMedia();\n        binding.getRoot().post(() -> {\n            TransitionManager.beginDelayedTransition(binding.getRoot());\n            if (detailsVisible) {\n                final Context context = getContext();\n                if (context == null) return;\n                originalRootBackground = binding.getRoot().getBackground();\n                final Resources resources = context.getResources();\n                if (resources == null) return;\n                final ColorDrawable colorDrawable = new ColorDrawable(resources.getColor(R.color.black));\n                binding.getRoot().setBackground(colorDrawable);\n                if (postView != null) {\n                    // Make post match parent\n                    final int fullHeight = Utils.displayMetrics.heightPixels - Utils.getStatusBarHeight(context);\n                    postView.getLayoutParams().height = fullHeight;\n                    binding.postContainer.getLayoutParams().height = fullHeight;\n                    if (playerView != null) {\n                        playerViewOriginalHeight = playerView.getLayoutParams().height;\n                        playerView.getLayoutParams().height = fullHeight;\n                    }\n                }\n                final BottomNavigationView bottomNavView = activity.getBottomNavView();\n                bottomNavView.setVisibility(View.GONE);\n                detailsVisible = false;\n                if (media.getUser() != null) {\n                    binding.profilePic.setVisibility(View.GONE);\n                    binding.title.setVisibility(View.GONE);\n                    binding.subtitle.setVisibility(View.GONE);\n                }\n                if (media.getLocation() != null) {\n                    binding.location.setVisibility(View.GONE);\n                }\n                if (media.getCaption() != null && !TextUtils.isEmpty(media.getCaption().getText())) {\n                    bottom.caption.setVisibility(View.GONE);\n                    bottom.translate.setVisibility(View.GONE);\n                }\n                bottom.likesCount.setVisibility(View.GONE);\n                bottom.commentsCount.setVisibility(View.GONE);\n                bottom.date.setVisibility(View.GONE);\n                bottom.comment.setVisibility(View.GONE);\n                bottom.like.setVisibility(View.GONE);\n                bottom.save.setVisibility(View.GONE);\n                bottom.share.setVisibility(View.GONE);\n                bottom.download.setVisibility(View.GONE);\n                binding.mediaCounter.setVisibility(View.GONE);\n                bottom.viewsCount.setVisibility(View.GONE);\n                final List<Integer> options = viewModel.getOptions().getValue();\n                if (options != null && !options.isEmpty()) {\n                    binding.options.setVisibility(View.GONE);\n                }\n                return;\n            }\n            if (originalRootBackground != null) {\n                binding.getRoot().setBackground(originalRootBackground);\n            }\n            if (postView != null) {\n                // Make post height back to original\n                postView.getLayoutParams().height = originalHeight;\n                binding.postContainer.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;\n                if (playerView != null) {\n                    playerView.getLayoutParams().height = playerViewOriginalHeight;\n                    playerView = null;\n                }\n            }\n            final BottomNavigationView bottomNavView = activity.getBottomNavView();\n            bottomNavView.setVisibility(View.VISIBLE);\n            if (media.getUser() != null) {\n                binding.profilePic.setVisibility(View.VISIBLE);\n                binding.title.setVisibility(View.VISIBLE);\n                binding.subtitle.setVisibility(View.VISIBLE);\n                // binding.topBg.setVisibility(View.VISIBLE);\n            }\n            if (media.getLocation() != null) {\n                binding.location.setVisibility(View.VISIBLE);\n            }\n            if (media.getCaption() != null && !TextUtils.isEmpty(media.getCaption().getText())) {\n                bottom.caption.setVisibility(View.VISIBLE);\n                bottom.translate.setVisibility(View.VISIBLE);\n            }\n            if (viewModel.hasPk()) {\n                bottom.likesCount.setVisibility(View.VISIBLE);\n                bottom.date.setVisibility(View.VISIBLE);\n                // binding.captionParent.setVisibility(View.VISIBLE);\n                // binding.captionToggle.setVisibility(View.VISIBLE);\n                bottom.share.setVisibility(View.VISIBLE);\n            }\n            if (viewModel.hasPk() && !viewModel.getMedia().getCommentsDisabled()) {\n                bottom.comment.setVisibility(View.VISIBLE);\n                bottom.commentsCount.setVisibility(View.VISIBLE);\n            }\n            bottom.download.setVisibility(View.VISIBLE);\n            final List<Integer> options = viewModel.getOptions().getValue();\n            if (options != null && !options.isEmpty()) {\n                binding.options.setVisibility(View.VISIBLE);\n            }\n            if (viewModel.isLoggedIn() && viewModel.hasPk()) {\n                bottom.like.setVisibility(View.VISIBLE);\n                bottom.save.setVisibility(View.VISIBLE);\n            }\n            // if (video) {\n            if (media.getType() == MediaItemType.MEDIA_TYPE_VIDEO) {\n                // binding.playerControlsToggle.setVisibility(View.VISIBLE);\n                bottom.viewsCount.setVisibility(View.VISIBLE);\n            }\n            // if (wasControlsVisible) {\n            //     showPlayerControls();\n            // }\n            if (media.getType() == MediaItemType.MEDIA_TYPE_SLIDER) {\n                binding.mediaCounter.setVisibility(View.VISIBLE);\n            }\n            detailsVisible = true;\n        });\n    }\n\n    private void hideSystemUI() {\n        if (detailsVisible) {\n            toggleDetails();\n        }\n        final MainActivity activity = (MainActivity) getActivity();\n        if (activity == null) return;\n        final ActionBar actionBar = activity.getSupportActionBar();\n        if (actionBar != null) {\n            actionBar.hide();\n        }\n        final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView();\n        appbarLayout.setVisibility(View.GONE);\n        final Toolbar toolbar = activity.getToolbar();\n        toolbar.setVisibility(View.GONE);\n        binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(),\n                                     binding.getRoot().getPaddingTop(),\n                                     binding.getRoot().getPaddingRight(),\n                                     0);\n        controller.hide(WindowInsetsCompat.Type.systemBars());\n        controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE);\n        isInFullScreenMode = true;\n    }\n\n    private void showSystemUI() {\n        if (!detailsVisible) {\n            toggleDetails();\n        }\n        final MainActivity activity = (MainActivity) getActivity();\n        if (activity == null) return;\n        final ActionBar actionBar = activity.getSupportActionBar();\n        if (actionBar != null) {\n            actionBar.show();\n        }\n        final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView();\n        appbarLayout.setVisibility(View.VISIBLE);\n        final Toolbar toolbar = activity.getToolbar();\n        toolbar.setVisibility(View.VISIBLE);\n        final Context context = getContext();\n        if (context == null) return;\n        binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(),\n                                     binding.getRoot().getPaddingTop(),\n                                     binding.getRoot().getPaddingRight(),\n                                     Utils.getActionBarHeight(context));\n        controller.show(WindowInsetsCompat.Type.systemBars());\n        WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false);\n        isInFullScreenMode = false;\n    }\n\n    private void navigateToProfile(final String username) {\n        final NavController navController = getNavController();\n        if (navController == null) return;\n        final NavDirections actionToProfile = PostViewV2FragmentDirections.actionToProfile().setUsername(username);\n        navController.navigate(actionToProfile);\n    }\n\n    @Nullable\n    private NavController getNavController() {\n        NavController navController = null;\n        try {\n            navController = NavHostFragment.findNavController(this);\n        } catch (IllegalStateException e) {\n            Log.e(TAG, \"navigateToProfile\", e);\n        }\n        return navController;\n    }\n\n    public boolean wasDeleted() {\n        return wasDeleted;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/SavedCollectionsFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.SavedStateHandle;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavBackStackEntry;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.FragmentNavigator;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.SavedCollectionsAdapter;\nimport awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;\nimport awais.instagrabber.databinding.FragmentSavedCollectionsBinding;\nimport awais.instagrabber.repositories.responses.saved.CollectionsListResponse;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.SavedCollectionsViewModel;\nimport awais.instagrabber.webservices.ProfileRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class SavedCollectionsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = SavedCollectionsFragment.class.getSimpleName();\n    public static boolean pleaseRefresh = false;\n\n    private MainActivity fragmentActivity;\n    private CoordinatorLayout root;\n    private FragmentSavedCollectionsBinding binding;\n    private SavedCollectionsViewModel savedCollectionsViewModel;\n    private boolean shouldRefresh = true;\n    private boolean isSaving;\n    private ProfileRepository profileRepository;\n    private SavedCollectionsAdapter adapter;\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        profileRepository = ProfileRepository.Companion.getInstance();\n        savedCollectionsViewModel = new ViewModelProvider(fragmentActivity).get(SavedCollectionsViewModel.class);\n        setHasOptionsMenu(true);\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentSavedCollectionsBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        setupObservers();\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.saved_collection_menu, menu);\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (pleaseRefresh) onRefresh();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.add) {\n            final Context context = getContext();\n            final EditText input = new EditText(context);\n            new AlertDialog.Builder(context)\n                    .setTitle(R.string.saved_create_collection)\n                    .setView(input)\n                    .setPositiveButton(R.string.confirm, (d, w) -> {\n                        final String cookie = settingsHelper.getString(Constants.COOKIE);\n                        profileRepository.createCollection(\n                                input.getText().toString(),\n                                settingsHelper.getString(Constants.DEVICE_UUID),\n                                CookieUtils.getUserIdFromCookie(cookie),\n                                CookieUtils.getCsrfTokenFromCookie(cookie),\n                                CoroutineUtilsKt.getContinuation((result, t) -> {\n                                    if (t != null) {\n                                        Log.e(TAG, \"Error creating collection\", t);\n                                        Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n                                        return;\n                                    }\n                                    onRefresh();\n                                })\n                        );\n                    })\n                    .setNegativeButton(R.string.cancel, null)\n                    .show();\n            return true;\n        }\n        return false;\n    }\n\n    private void init() {\n        setupTopics();\n        fetchTopics(null);\n        final SavedCollectionsFragmentArgs fragmentArgs = SavedCollectionsFragmentArgs.fromBundle(getArguments());\n        isSaving = fragmentArgs.getIsSaving();\n    }\n\n    @Override\n    public void onRefresh() {\n        fetchTopics(null);\n    }\n\n    public void setupTopics() {\n        binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2)));\n        adapter = new SavedCollectionsAdapter((topicCluster, root, cover, title, titleColor, backgroundColor) -> {\n            final NavController navController = NavHostFragment.findNavController(this);\n            if (isSaving) {\n                setNavControllerResult(navController, topicCluster.getCollectionId());\n                navController.navigateUp();\n            } else {\n                try {\n                    final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()\n                            .addSharedElement(cover, \"collection-\" + topicCluster.getCollectionId());\n                    final NavDirections action = SavedCollectionsFragmentDirections\n                            .actionToCollectionPosts(topicCluster, titleColor, backgroundColor);\n                    navController.navigate(action, builder.build());\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupTopics: \", e);\n                }\n            }\n        });\n        binding.topicsRecyclerView.setAdapter(adapter);\n    }\n\n    private void setupObservers() {\n        savedCollectionsViewModel.getList().observe(getViewLifecycleOwner(), list -> {\n            if (adapter == null) return;\n            adapter.submitList(list);\n        });\n    }\n\n    private void fetchTopics(final String maxId) {\n        binding.swipeRefreshLayout.setRefreshing(true);\n        profileRepository.fetchCollections(maxId, CoroutineUtilsKt.getContinuation((result, t) -> {\n            if (t != null) {\n                Log.e(TAG, \"onFailure\", t);\n                binding.swipeRefreshLayout.setRefreshing(false);\n                return;\n            }\n            if (result == null) return;\n            savedCollectionsViewModel.getList().postValue(result.getItems());\n            binding.swipeRefreshLayout.setRefreshing(false);\n        }));\n    }\n\n    private void setNavControllerResult(@NonNull final NavController navController, final String result) {\n        final NavBackStackEntry navBackStackEntry = navController.getPreviousBackStackEntry();\n        if (navBackStackEntry == null) return;\n        final SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();\n        savedStateHandle.set(\"collection\", result);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.asyncs.SavedPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentSavedBinding;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.fragments.main.ProfileFragmentDirections;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.PostItemType;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = SavedViewerFragment.class.getSimpleName();\n\n    private FragmentSavedBinding binding;\n    private String username;\n    private long profileId;\n    private ActionMode actionMode;\n    private SwipeRefreshLayout root;\n    private AppCompatActivity fragmentActivity;\n    private boolean isLoggedIn, shouldRefresh = true;\n    private PostItemType type;\n    private Set<Media> selectedFeedModels;\n    private PostsLayoutPreferences layoutPreferences;\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.posts.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.multi_select_download_menu,\n            new PrimaryActionModeCallback.CallbacksHelper() {\n                @Override\n                public void onDestroy(final ActionMode mode) {\n                    binding.posts.endSelection();\n                }\n\n                @Override\n                public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n                    if (item.getItemId() == R.id.action_download) {\n                        if (SavedViewerFragment.this.selectedFeedModels == null) return false;\n                        final Context context = getContext();\n                        if (context == null) return false;\n                        DownloadUtils.download(context, ImmutableList.copyOf(SavedViewerFragment.this.selectedFeedModels));\n                        binding.posts.endSelection();\n                    }\n                    return false;\n                }\n            });\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            try {\n                final NavDirections commentsAction = ProfileFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getPk(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(SavedViewerFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = ProfileFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media feedModel) {\n            final Location location = feedModel.getLocation();\n            if (location == null) return;\n            try {\n                final NavDirections action = ProfileFragmentDirections.actionToLocation(location.getPk());\n                NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            navigateToProfile(\"@\" + feedModel.getUser().getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(final Media feedModel, final int position) {\n            try {\n                final NavDirections action = SavedViewerFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n        }\n    };\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            SavedViewerFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (AppCompatActivity) getActivity();\n        setHasOptionsMenu(true);\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentSavedBinding.inflate(getLayoutInflater(), container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.saved_viewer_menu, menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        setTitle();\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.posts.refresh();\n    }\n\n    private void init() {\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final SavedViewerFragmentArgs fragmentArgs = SavedViewerFragmentArgs.fromBundle(arguments);\n        username = fragmentArgs.getUsername();\n        profileId = fragmentArgs.getProfileId();\n        type = fragmentArgs.getType();\n        layoutPreferences = Utils.getPostsLayoutPreferences(getPostsLayoutPreferenceKey());\n        setupPosts();\n    }\n\n    private void setupPosts() {\n        binding.posts.setViewModelStoreOwner(this)\n                     .setLifeCycleOwner(this)\n                     .setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn, null))\n                     .setLayoutPreferences(layoutPreferences)\n                     .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                     .setFeedItemCallback(feedItemCallback)\n                     .setSelectionModeCallback(selectionModeCallback)\n                     .init();\n        binding.swipeRefreshLayout.setRefreshing(true);\n    }\n\n    @NonNull\n    private String getPostsLayoutPreferenceKey() {\n        switch (type) {\n            case LIKED:\n                return Constants.PREF_LIKED_POSTS_LAYOUT;\n            case TAGGED:\n                return Constants.PREF_TAGGED_POSTS_LAYOUT;\n            case SAVED:\n            default:\n                return Constants.PREF_SAVED_POSTS_LAYOUT;\n        }\n    }\n\n    private void setTitle() {\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        if (actionBar == null) return;\n        final int titleRes;\n        switch (type) {\n            case LIKED:\n                titleRes = R.string.liked;\n                break;\n            case TAGGED:\n                titleRes = R.string.tagged;\n                break;\n            default:\n            case SAVED:\n                titleRes = R.string.saved;\n                break;\n        }\n        actionBar.setTitle(titleRes);\n        actionBar.setSubtitle(username);\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() ->\n                binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())\n        );\n    }\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = SavedViewerFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                getPostsLayoutPreferenceKey(),\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);\n                });\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java",
    "content": "package awais.instagrabber.fragments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.SearchView;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.common.collect.Iterables;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.FeedStoriesListAdapter;\nimport awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;\nimport awais.instagrabber.adapters.HighlightStoriesListAdapter;\nimport awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoader;\nimport awais.instagrabber.databinding.FragmentStoryListViewerBinding;\nimport awais.instagrabber.repositories.requests.StoryViewerOptions;\nimport awais.instagrabber.repositories.responses.stories.ArchiveResponse;\nimport awais.instagrabber.repositories.responses.stories.Story;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.viewmodels.ArchivesViewModel;\nimport awais.instagrabber.viewmodels.FeedStoriesViewModel;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport awais.instagrabber.webservices.StoriesRepository;\nimport kotlinx.coroutines.Dispatchers;\n\npublic final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"StoryListViewerFragment\";\n\n    private AppCompatActivity fragmentActivity;\n    private FragmentStoryListViewerBinding binding;\n    private SwipeRefreshLayout root;\n    private boolean shouldRefresh = true;\n    private boolean firstRefresh = true;\n    private FeedStoriesViewModel feedStoriesViewModel;\n    private ArchivesViewModel archivesViewModel;\n    private StoriesRepository storiesRepository;\n    private Context context;\n    private String type;\n    private String endCursor = null;\n    private FeedStoriesListAdapter adapter;\n\n    private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() {\n        @Override\n        public void onFeedStoryClick(final Story model) {\n            if (model == null) return;\n            final List<Story> feedStoryModels = feedStoriesViewModel.getList().getValue();\n            if (feedStoryModels == null) return;\n            final int position = Iterables.indexOf(feedStoryModels, feedStoryModel -> feedStoryModel != null\n                    && Objects.equals(feedStoryModel.getId(), model.getId()));\n            try {\n                final NavDirections action = StoryListViewerFragmentDirections.actionToStory(StoryViewerOptions.forFeedStoryPosition(position));\n                NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onFeedStoryClick: \", e);\n            }\n        }\n\n        @Override\n        public void onProfileClick(final String username) {\n            openProfile(username);\n        }\n    };\n\n    private final OnHighlightStoryClickListener archiveClickListener = new OnHighlightStoryClickListener() {\n        @Override\n        public void onHighlightClick(final Story model, final int position) {\n            if (model == null) return;\n            try {\n                final NavDirections action = StoryListViewerFragmentDirections.actionToStory(StoryViewerOptions.forStoryArchive(position));\n                NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHighlightClick: \", e);\n            }\n        }\n\n        @Override\n        public void onProfileClick(final String username) {\n            openProfile(username);\n        }\n    };\n\n    private final ServiceCallback<ArchiveResponse> cb = new ServiceCallback<ArchiveResponse>() {\n        @Override\n        public void onSuccess(final ArchiveResponse result) {\n            binding.swipeRefreshLayout.setRefreshing(false);\n            if (result == null) {\n                try {\n                    final Context context = getContext();\n                    Toast.makeText(context, R.string.empty_list, Toast.LENGTH_SHORT).show();\n                } catch (Exception ignored) {}\n            } else {\n                endCursor = result.getMaxId();\n                final List<Story> models = archivesViewModel.getList().getValue();\n                final List<Story> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);\n                if (result.getItems() != null) modelsCopy.addAll(result.getItems());\n                archivesViewModel.getList().postValue(modelsCopy);\n            }\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            Log.e(TAG, \"Error\", t);\n            try {\n                final Context context = getContext();\n                Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n            } catch (Exception ignored) {}\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (AppCompatActivity) requireActivity();\n        context = getContext();\n        if (context == null) return;\n        final Bundle args = getArguments();\n        if (args == null) return;\n        final StoryListViewerFragmentArgs fragmentArgs = StoryListViewerFragmentArgs.fromBundle(args);\n        type = fragmentArgs.getType();\n        setHasOptionsMenu(type.equals(\"feed\"));\n        storiesRepository = StoriesRepository.Companion.getInstance();\n    }\n\n    @NonNull\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentStoryListViewerBinding.inflate(getLayoutInflater());\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater inflater) {\n        inflater.inflate(R.menu.search, menu);\n        final MenuItem menuSearch = menu.findItem(R.id.action_search);\n        final SearchView searchView = (SearchView) menuSearch.getActionView();\n        searchView.setQueryHint(getResources().getString(R.string.action_search));\n        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {\n\n            @Override\n            public boolean onQueryTextSubmit(final String query) {\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextChange(final String query) {\n                if (adapter != null) {\n                    adapter.getFilter().filter(query);\n                }\n                return true;\n            }\n        });\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        if (actionBar != null) actionBar.setTitle(type.equals(\"feed\") ? R.string.feed_stories : R.string.action_archive);\n    }\n\n    @Override\n    public void onDestroy() {\n        if (archivesViewModel != null) archivesViewModel.getList().postValue(null);\n        super.onDestroy();\n    }\n\n    private void init() {\n        final Context context = getContext();\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        final LinearLayoutManager layoutManager = new LinearLayoutManager(context);\n        final ActionBar actionBar = fragmentActivity.getSupportActionBar();\n        if (type.equals(\"feed\")) {\n            if (actionBar != null) actionBar.setTitle(R.string.feed_stories);\n            feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);\n            adapter = new FeedStoriesListAdapter(clickListener);\n            binding.rvStories.setLayoutManager(layoutManager);\n            binding.rvStories.setAdapter(adapter);\n            feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), list -> {\n                if (list == null) {\n                    adapter.submitList(Collections.emptyList());\n                    return;\n                }\n                adapter.submitList(list);\n            });\n        } else {\n            if (actionBar != null) actionBar.setTitle(R.string.action_archive);\n            final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {\n                if (!TextUtils.isEmpty(endCursor)) onRefresh();\n                endCursor = null;\n            });\n            binding.rvStories.addOnScrollListener(lazyLoader);\n            archivesViewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class);\n            final HighlightStoriesListAdapter adapter = new HighlightStoriesListAdapter(archiveClickListener);\n            binding.rvStories.setLayoutManager(layoutManager);\n            binding.rvStories.setAdapter(adapter);\n            archivesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);\n        }\n        onRefresh();\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.swipeRefreshLayout.setRefreshing(true);\n        if (type.equals(\"feed\") && firstRefresh) {\n            binding.swipeRefreshLayout.setRefreshing(false);\n            final List<Story> value = feedStoriesViewModel.getList().getValue();\n            if (value != null) {\n                adapter.submitList(value);\n            }\n            firstRefresh = false;\n        } else if (type.equals(\"feed\")) {\n            storiesRepository.getFeedStories(\n                    CoroutineUtilsKt.getContinuation((feedStoryModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            Log.e(TAG, \"failed\", throwable);\n                            Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show();\n                            return;\n                        }\n                        //noinspection unchecked\n                        feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);\n                        //noinspection unchecked\n                        adapter.submitList((List<Story>) feedStoryModels);\n                        binding.swipeRefreshLayout.setRefreshing(false);\n                    }), Dispatchers.getIO())\n            );\n        } else if (type.equals(\"archive\")) {\n            storiesRepository.fetchArchive(\n                    endCursor,\n                    CoroutineUtilsKt.getContinuation((archiveFetchResponse, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            cb.onFailure(throwable);\n                            return;\n                        }\n                        cb.onSuccess(archiveFetchResponse);\n                    }), Dispatchers.getIO())\n            );\n        }\n    }\n\n    private void openProfile(final String username) {\n        try {\n            final NavDirections action = StoryListViewerFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"openProfile: \", e);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt",
    "content": "package awais.instagrabber.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.DialogInterface.OnClickListener\nimport android.graphics.drawable.Animatable\nimport android.net.Uri\nimport android.os.Bundle\nimport android.os.Handler\nimport android.util.Log\nimport android.view.*\nimport android.view.GestureDetector.SimpleOnGestureListener\nimport android.widget.*\nimport android.widget.SeekBar.OnSeekBarChangeListener\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.view.ContextThemeWrapper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.appcompat.widget.TooltipCompat\nimport androidx.core.view.GestureDetectorCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.navigation.NavController\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.R\nimport awais.instagrabber.adapters.StoriesAdapter\nimport awais.instagrabber.customviews.helpers.SwipeGestureListener\nimport awais.instagrabber.databinding.FragmentStoryViewerBinding\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.interfaces.SwipeEvent\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.models.enums.StoryPaginationType\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.repositories.responses.stories.*\nimport awais.instagrabber.utils.DownloadUtils.download\nimport awais.instagrabber.utils.ResponseBodyUtils\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.viewmodels.ArchivesViewModel\nimport awais.instagrabber.viewmodels.FeedStoriesViewModel\nimport awais.instagrabber.viewmodels.StoryFragmentViewModel\nimport awais.instagrabber.webservices.MediaRepository\nimport awais.instagrabber.webservices.StoriesRepository\nimport com.facebook.drawee.backends.pipeline.Fresco\nimport com.facebook.drawee.controller.BaseControllerListener\nimport com.facebook.drawee.interfaces.DraweeController\nimport com.facebook.imagepipeline.image.ImageInfo\nimport com.facebook.imagepipeline.request.ImageRequestBuilder\nimport com.google.android.exoplayer2.MediaItem\nimport com.google.android.exoplayer2.Player\nimport com.google.android.exoplayer2.SimpleExoPlayer\nimport com.google.android.exoplayer2.source.*\nimport com.google.android.exoplayer2.source.dash.DashMediaSource\nimport com.google.android.exoplayer2.upstream.DefaultDataSourceFactory\nimport com.google.android.material.textfield.TextInputEditText\nimport java.io.IOException\nimport java.text.NumberFormat\nimport java.util.*\n\n\nclass StoryViewerFragment : Fragment() {\n    private val TAG = \"StoryViewerFragment\"\n\n    private var root: View? = null\n    private var currentStoryUsername: String? = null\n    private var storiesAdapter: StoriesAdapter? = null\n    private var swipeEvent: SwipeEvent? = null\n    private var gestureDetector: GestureDetectorCompat? = null\n    private val storiesRepository: StoriesRepository? = null\n    private val mediaRepository: MediaRepository? = null\n    private var menuProfile: MenuItem? = null\n    private var profileVisible: Boolean = false\n    private var player: SimpleExoPlayer? = null\n\n    private var shouldRefresh = true\n    private var currentFeedStoryIndex = 0\n    private var sliderValue = 0.0\n    private var options: StoryViewerOptions? = null\n    private var listViewModel: ViewModel? = null\n    private var backStackSavedStateResultLiveData: MutableLiveData<Any?>? = null\n    private lateinit var fragmentActivity: AppCompatActivity\n    private lateinit var storiesViewModel: StoryFragmentViewModel\n    private lateinit var binding: FragmentStoryViewerBinding\n\n    @Suppress(\"UNCHECKED_CAST\")\n    private val backStackSavedStateObserver = Observer<Any?> { result ->\n        if (result == null) return@Observer\n        if ((result is RankedRecipient)) {\n            if (context != null) {\n                Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show()\n            }\n            storiesViewModel.shareDm(result)\n        } else if ((result is Set<*>)) {\n            try {\n                if (context != null) {\n                    Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show()\n                }\n                storiesViewModel.shareDm(result as Set<RankedRecipient>)\n            } catch (e: Exception) {\n                Log.e(TAG, \"share: \", e)\n            }\n        }\n        // clear result\n        backStackSavedStateResultLiveData?.postValue(null)\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        fragmentActivity = requireActivity() as AppCompatActivity\n        storiesViewModel = ViewModelProvider(this).get(StoryFragmentViewModel::class.java)\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View? {\n        if (root != null) {\n            shouldRefresh = false\n            return root\n        }\n        binding = FragmentStoryViewerBinding.inflate(inflater, container, false)\n        root = binding.root\n        return root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        if (!shouldRefresh) return\n        init()\n        shouldRefresh = false\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {\n        menuInflater.inflate(R.menu.story_menu, menu)\n        menuProfile = menu.findItem(R.id.action_profile)\n        menuProfile!!.isVisible = profileVisible\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        val itemId = item.itemId\n        if (itemId == R.id.action_profile) {\n            val username = storiesViewModel.getCurrentStory().value?.user?.username\n            openProfile(Pair(username, FavoriteType.USER))\n            return true\n        }\n        return false\n    }\n\n    override fun onPause() {\n        super.onPause()\n        player?.pause() ?: return\n    }\n\n    override fun onResume() {\n        super.onResume()\n        setHasOptionsMenu(true)\n        try {\n            val backStackEntry = NavHostFragment.findNavController(this).currentBackStackEntry\n            if (backStackEntry != null) {\n                backStackSavedStateResultLiveData = backStackEntry.savedStateHandle.getLiveData(\"result\")\n                backStackSavedStateResultLiveData?.observe(viewLifecycleOwner, backStackSavedStateObserver)\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"onResume: \", e)\n        }\n        val actionBar = fragmentActivity.supportActionBar ?: return\n        actionBar.title = storiesViewModel.getTitle().value\n        actionBar.subtitle = storiesViewModel.getDate().value\n    }\n\n    override fun onDestroy() {\n        releasePlayer()\n        val actionBar = fragmentActivity.supportActionBar\n        actionBar?.subtitle = null\n        super.onDestroy()\n    }\n\n    private fun init() {\n        val args = arguments ?: return\n        val fragmentArgs = StoryViewerFragmentArgs.fromBundle(args)\n        options = fragmentArgs.options\n        currentFeedStoryIndex = options!!.currentFeedStoryIndex\n        val type = options!!.type\n        if (currentFeedStoryIndex >= 0) {\n            listViewModel = when (type) {\n                StoryViewerOptions.Type.STORY_ARCHIVE ->\n                    ViewModelProvider(fragmentActivity).get(ArchivesViewModel::class.java)\n                StoryViewerOptions.Type.FEED_STORY_POSITION ->\n                    ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel::class.java)\n                else -> null\n            }\n        }\n        setupButtons()\n        setupStories()\n    }\n\n    private fun setupStories() {\n        setupListeners()\n        val context = context ?: return\n        binding.storiesList.layoutManager =\n            LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)\n        storiesAdapter = StoriesAdapter { _, position ->\n            storiesViewModel.setMedia(position)\n        }\n        binding.storiesList.adapter = storiesAdapter\n        storiesViewModel.getCurrentStory().observe(fragmentActivity, {\n            if (it?.items != null && it.items.size > 1) {\n                val storyMedias = it.items.toMutableList()\n                val newItem = storyMedias.get(0)\n                newItem.isCurrentSlide = true\n                storyMedias.set(0, newItem)\n                storiesAdapter!!.submitList(storyMedias)\n                storiesViewModel.setMedia(0)\n                binding.listToggle.isEnabled = true\n                binding.storiesList.visibility = if (Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_STORY_SHOW_LIST)) View.VISIBLE\n                else View.GONE\n            }\n            else {\n                if (it?.items != null) storiesViewModel.setMedia(0)\n                binding.listToggle.isEnabled = false\n                binding.storiesList.visibility = View.GONE\n            }\n        })\n        storiesViewModel.getDate().observe(fragmentActivity, {\n            val actionBar = fragmentActivity.supportActionBar\n            if (actionBar != null && it != null) actionBar.subtitle = it\n        })\n        storiesViewModel.getTitle().observe(fragmentActivity, {\n            val actionBar = fragmentActivity.supportActionBar\n            if (actionBar != null && it != null) actionBar.title = it\n        })\n        storiesViewModel.getCurrentMedia().observe(fragmentActivity, { refreshStory(it) })\n        storiesViewModel.getCurrentIndex().observe(fragmentActivity, {\n            storiesAdapter!!.paginate(it)\n        })\n        storiesViewModel.getOptions().observe(fragmentActivity, {\n            binding.stickers.isEnabled = it.first.size > 0\n        })\n    }\n\n    private fun setupButtons() {\n        binding.btnDownload.setOnClickListener({ _ -> downloadStory() })\n        binding.btnForward.setOnClickListener({ _ -> storiesViewModel.skip(false) })\n        binding.btnBackward.setOnClickListener({ _ -> storiesViewModel.skip(true) })\n        binding.btnShare.setOnClickListener({ _ -> shareStoryViaDm() })\n        binding.btnReply.setOnClickListener({ _ -> createReplyDialog(null) })\n        binding.stickers.setOnClickListener({ _ -> showStickerMenu() })\n        binding.listToggle.setOnClickListener({ _ ->\n            binding.storiesList.visibility = if (binding.storiesList.visibility == View.GONE) View.VISIBLE\n            else View.GONE\n        })\n\n        TooltipCompat.setTooltipText(binding.btnDownload, getString(R.string.action_download))\n        TooltipCompat.setTooltipText(binding.btnShare, getString(R.string.share))\n        TooltipCompat.setTooltipText(binding.btnReply, getString(R.string.reply_story))\n        TooltipCompat.setTooltipText(binding.stickers, getString(R.string.story_stickers))\n        TooltipCompat.setTooltipText(binding.listToggle, getString(R.string.story_list))\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    private fun setupListeners() {\n        if (currentFeedStoryIndex >= 0) {\n            val type = options!!.type\n            when (type) {\n                StoryViewerOptions.Type.HIGHLIGHT -> {\n                    storiesViewModel.fetchHighlights(options!!.id)\n                    storiesViewModel.highlights.observe(fragmentActivity) {\n                        setupMultipage(it)\n                    }\n                }\n                StoryViewerOptions.Type.FEED_STORY_POSITION -> {\n                    val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?\n                    setupMultipage(feedStoriesViewModel!!.list.value)\n                }\n                StoryViewerOptions.Type.STORY_ARCHIVE -> {\n                    val archivesViewModel = listViewModel as ArchivesViewModel?\n                    setupMultipage(archivesViewModel!!.list.value)\n                }\n                StoryViewerOptions.Type.USER -> {\n                    resetView()\n                }\n            }\n        }\n\n        val context = context ?: return\n        swipeEvent = SwipeEvent { isRightSwipe: Boolean ->\n            storiesViewModel.paginate(isRightSwipe)\n        }\n        gestureDetector = GestureDetectorCompat(context, SwipeGestureListener(swipeEvent))\n        binding.playerView.setOnTouchListener { _, event -> gestureDetector!!.onTouchEvent(event) }\n        val simpleOnGestureListener: SimpleOnGestureListener = object : SimpleOnGestureListener() {\n            override fun onFling(\n                e1: MotionEvent,\n                e2: MotionEvent,\n                velocityX: Float,\n                velocityY: Float\n            ): Boolean {\n                val diffX = e2.x - e1.x\n                try {\n                    if (Math.abs(diffX) > Math.abs(e2.y - e1.y) && Math.abs(diffX) > SwipeGestureListener.SWIPE_THRESHOLD && Math.abs(\n                            velocityX\n                        ) > SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD\n                    ) {\n                        storiesViewModel.paginate(diffX > 0)\n                        return true\n                    }\n                } catch (e: Exception) {\n                    if (BuildConfig.DEBUG) Log.e(TAG, \"Error\", e)\n                }\n                return false\n            }\n        }\n        binding.imageViewer.setTapListener(simpleOnGestureListener)\n    }\n\n    private fun setupMultipage(models: List<Story>?) {\n        if (models == null) return\n        storiesViewModel.getPagination().observe(fragmentActivity, {\n            when (it) {\n                StoryPaginationType.FORWARD -> {\n                    if (currentFeedStoryIndex == models.size - 1)\n                        Toast.makeText(\n                            context,\n                            R.string.no_more_stories,\n                            Toast.LENGTH_SHORT\n                        ).show()\n                    else paginateStories(false, currentFeedStoryIndex == models.size - 2)\n                }\n                StoryPaginationType.BACKWARD -> {\n                    if (currentFeedStoryIndex == 0)\n                        Toast.makeText(\n                            context,\n                            R.string.no_more_stories,\n                            Toast.LENGTH_SHORT\n                        ).show()\n                    else paginateStories(true, false)\n                }\n                StoryPaginationType.ERROR -> {\n                    Toast.makeText(\n                        context,\n                        R.string.downloader_unknown_error,\n                        Toast.LENGTH_SHORT\n                    ).show()\n                }\n            }\n        })\n        if (!models.isEmpty()) {\n            binding.btnBackward.isEnabled = currentFeedStoryIndex != 0\n            binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1\n            resetView()\n        }\n    }\n\n    private fun resetView() {\n        val context = context ?: return\n        if (menuProfile != null) menuProfile!!.isVisible = false\n        binding.imageViewer.controller = null\n        releasePlayer()\n        val type = options!!.type\n        var fetchOptions: StoryViewerOptions? = null\n        when (type) {\n            StoryViewerOptions.Type.HIGHLIGHT -> {\n                val models = storiesViewModel.highlights.value\n                if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) {\n                    Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show()\n                    return\n                }\n                fetchOptions = StoryViewerOptions.forHighlight(0L, models[currentFeedStoryIndex].id)\n            }\n            StoryViewerOptions.Type.FEED_STORY_POSITION -> {\n                val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?\n                val models = feedStoriesViewModel!!.list.value\n                if (models == null || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) return\n                val userStory = models[currentFeedStoryIndex]\n                currentStoryUsername = userStory.user!!.username\n                fetchOptions = StoryViewerOptions.forUser(userStory.user.pk, currentStoryUsername)\n                val live = userStory.broadcast\n                if (live != null) {\n                    storiesViewModel.setStory(userStory)\n                    refreshLive(live)\n                    return\n                }\n            }\n            StoryViewerOptions.Type.STORY_ARCHIVE -> {\n                val archivesViewModel = listViewModel as ArchivesViewModel?\n                val models = archivesViewModel!!.list.value\n                if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) {\n                    Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT)\n                        .show()\n                    return\n                }\n                val (id, _, _, _, _, _, _, _, _, title) = models[currentFeedStoryIndex]\n                currentStoryUsername = title\n                fetchOptions = StoryViewerOptions.forStoryArchive(id)\n            }\n            StoryViewerOptions.Type.USER -> {\n                currentStoryUsername = options!!.name\n                fetchOptions = StoryViewerOptions.forUser(options!!.id, currentStoryUsername)\n            }\n        }\n        if (type == StoryViewerOptions.Type.STORY) {\n            storiesViewModel.fetchSingleMedia(options!!.id)\n            return\n        }\n        storiesViewModel.fetchStory(fetchOptions).observe(viewLifecycleOwner, {\n            if (it.status == Resource.Status.ERROR) {\n                Toast.makeText(context, \"Error: \" + it.message, Toast.LENGTH_SHORT).show()\n            }\n        })\n    }\n\n    @Synchronized\n    private fun refreshLive(live: Broadcast) {\n        binding.btnDownload.isEnabled = false\n        binding.stickers.isEnabled = false\n        binding.listToggle.isEnabled = false\n        binding.btnShare.isEnabled = false\n        binding.btnReply.isEnabled = false\n        releasePlayer()\n        setupLive(live.dashPlaybackUrl ?: live.dashAbrPlaybackUrl ?: return)\n    }\n\n    @Synchronized\n    private fun refreshStory(currentStory: StoryMedia) {\n        val itemType = currentStory.type\n        val url = if (itemType === MediaItemType.MEDIA_TYPE_IMAGE) ResponseBodyUtils.getImageUrl(currentStory)\n                  else ResponseBodyUtils.getVideoUrl(currentStory)\n\n        releasePlayer()\n\n        profileVisible = currentStory.user?.username != null\n        if (menuProfile != null) menuProfile!!.isVisible = profileVisible\n\n        binding.btnDownload.isEnabled = false\n        binding.btnShare.isEnabled = currentStory.canReshare\n        binding.btnReply.isEnabled = currentStory.canReply\n        if (itemType === MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(url) else setupImage(url)\n\n        if (options!!.type == StoryViewerOptions.Type.FEED_STORY_POSITION\n            && Utils.settingsHelper.getBoolean(PreferenceKeys.MARK_AS_SEEN)) {\n                val feedStoriesViewModel = listViewModel as FeedStoriesViewModel?\n                storiesViewModel.markAsSeen(currentStory).observe(viewLifecycleOwner) { m ->\n                    if (m.status == Resource.Status.SUCCESS && m.data != null) {\n                        val liveModels: MutableLiveData<List<Story>> = feedStoriesViewModel!!.list\n                        val models = liveModels.value\n                        val modelsCopy: MutableList<Story> = models!!.toMutableList()\n                        modelsCopy.set(currentFeedStoryIndex, m.data)\n                        liveModels.postValue(modelsCopy)\n                    }\n                }\n        }\n    }\n\n    private fun downloadStory() {\n        val context = context ?: return\n        val currentStory = storiesViewModel.getMedia().value\n        if (currentStory == null) {\n            Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show()\n            return\n        }\n        download(context, currentStory)\n    }\n\n    private fun setupImage(url: String) {\n        binding.progressView.visibility = View.VISIBLE\n        binding.playerView.visibility = View.GONE\n        binding.imageViewer.visibility = View.VISIBLE\n        val requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))\n            .setLocalThumbnailPreviewsEnabled(true)\n            .setProgressiveRenderingEnabled(true)\n            .build()\n        val controller: DraweeController = Fresco.newDraweeControllerBuilder()\n            .setImageRequest(requestBuilder)\n            .setOldController(binding.imageViewer.controller)\n            .setControllerListener(object : BaseControllerListener<ImageInfo?>() {\n                override fun onFailure(id: String, throwable: Throwable) {\n                    binding.btnDownload.isEnabled = false\n                    binding.progressView.visibility = View.GONE\n                }\n\n                override fun onFinalImageSet(\n                    id: String,\n                    imageInfo: ImageInfo?,\n                    animatable: Animatable?\n                ) {\n                    binding.btnDownload.isEnabled = true\n                    binding.progressView.visibility = View.GONE\n                }\n            })\n            .build()\n        binding.imageViewer.controller = controller\n    }\n\n    private fun setupVideo(url: String) {\n        binding.playerView.visibility = View.VISIBLE\n        binding.progressView.visibility = View.GONE\n        binding.imageViewer.visibility = View.GONE\n        binding.imageViewer.controller = null\n        val context = context ?: return\n        player = SimpleExoPlayer.Builder(context).build()\n        binding.playerView.player = player\n        player!!.playWhenReady =\n            Utils.settingsHelper.getBoolean(PreferenceKeys.AUTOPLAY_VIDEOS_STORIES)\n        val uri = Uri.parse(url)\n        val mediaItem = MediaItem.fromUri(uri)\n        val mediaSource =\n            ProgressiveMediaSource.Factory(DefaultDataSourceFactory(context, \"instagram\"))\n                .createMediaSource(mediaItem)\n        mediaSource.addEventListener(Handler(), object : MediaSourceEventListener {\n            override fun onLoadCompleted(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.btnDownload.isEnabled = true\n                binding.progressView.visibility = View.GONE\n            }\n\n            override fun onLoadStarted(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.btnDownload.isEnabled = true\n                binding.progressView.visibility = View.VISIBLE\n            }\n\n            override fun onLoadCanceled(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.progressView.visibility = View.GONE\n            }\n\n            override fun onLoadError(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData,\n                error: IOException,\n                wasCanceled: Boolean\n            ) {\n                binding.btnDownload.isEnabled = false\n                binding.progressView.visibility = View.GONE\n            }\n        })\n        player!!.setMediaSource(mediaSource)\n        player!!.prepare()\n        binding.playerView.setOnClickListener { _ ->\n            if (player != null) {\n                if (player!!.playbackState == Player.STATE_ENDED) player!!.seekTo(0)\n                player!!.playWhenReady =\n                    player!!.playbackState == Player.STATE_ENDED || !player!!.isPlaying\n            }\n        }\n    }\n\n    private fun setupLive(url: String) {\n        binding.playerView.visibility = View.VISIBLE\n        binding.progressView.visibility = View.GONE\n        binding.imageViewer.visibility = View.GONE\n        binding.imageViewer.controller = null\n        val context = context ?: return\n        player = SimpleExoPlayer.Builder(context).build()\n        binding.playerView.player = player\n        player!!.playWhenReady =\n            Utils.settingsHelper.getBoolean(PreferenceKeys.AUTOPLAY_VIDEOS_STORIES)\n        val uri = Uri.parse(url)\n        val mediaItem = MediaItem.fromUri(uri)\n        val mediaSource = DashMediaSource.Factory(DefaultDataSourceFactory(context, \"instagram\"))\n            .createMediaSource(mediaItem)\n        mediaSource.addEventListener(Handler(), object : MediaSourceEventListener {\n            override fun onLoadCompleted(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.progressView.visibility = View.GONE\n            }\n\n            override fun onLoadStarted(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.progressView.visibility = View.VISIBLE\n            }\n\n            override fun onLoadCanceled(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData\n            ) {\n                binding.progressView.visibility = View.GONE\n            }\n\n            override fun onLoadError(\n                windowIndex: Int,\n                mediaPeriodId: MediaSource.MediaPeriodId?,\n                loadEventInfo: LoadEventInfo,\n                mediaLoadData: MediaLoadData,\n                error: IOException,\n                wasCanceled: Boolean\n            ) {\n                binding.progressView.visibility = View.GONE\n            }\n        })\n        player!!.setMediaSource(mediaSource)\n        player!!.prepare()\n        binding.playerView.setOnClickListener { _ ->\n            if (player != null) {\n                if (player!!.playbackState == Player.STATE_ENDED) player!!.seekTo(0)\n                player!!.playWhenReady =\n                    player!!.playbackState == Player.STATE_ENDED || !player!!.isPlaying\n            }\n        }\n    }\n\n    private fun openProfile(data: Pair<String?, FavoriteType>) {\n        val navController: NavController = NavHostFragment.findNavController(this)\n        val bundle = Bundle()\n        if (data.first == null) {\n            // toast\n            return\n        }\n        val actionBar = fragmentActivity.supportActionBar\n        if (actionBar != null) {\n            actionBar.title = null\n            actionBar.subtitle = null\n        }\n        val action = when (data.second) {\n            FavoriteType.USER -> {\n                StoryViewerFragmentDirections.actionToProfile().apply { this.username = data.first!! }\n            }\n            FavoriteType.HASHTAG -> {\n                StoryViewerFragmentDirections.actionToHashtag(data.first!!)\n            }\n            FavoriteType.LOCATION -> {\n                StoryViewerFragmentDirections.actionToLocation(data.first!!.toLong())\n            }\n            else -> null\n        }\n        navController.navigate(action!!)\n    }\n\n    private fun releasePlayer() {\n        if (player == null) return\n        try {\n            player!!.stop(true)\n        } catch (ignored: Exception) {\n        }\n        try {\n            player!!.release()\n        } catch (ignored: Exception) {\n        }\n        player = null\n    }\n\n    private fun paginateStories(\n        backward: Boolean,\n        last: Boolean\n    ) {\n        binding.btnBackward.isEnabled = currentFeedStoryIndex != 1 || !backward\n        binding.btnForward.isEnabled = !last\n        currentFeedStoryIndex = if (backward) currentFeedStoryIndex - 1 else currentFeedStoryIndex + 1\n        resetView()\n    }\n\n    private fun createChoiceDialog(\n        title: String?,\n        tallies: List<Tally>,\n        onClickListener: OnClickListener,\n        viewerVote: Int?,\n        correctAnswer: Int?\n    ) {\n        val context = context ?: return\n        val choices = tallies.map {\n            (if (viewerVote == tallies.indexOf(it)) \"√ \" else \"\") +\n            (if (correctAnswer == tallies.indexOf(it)) \"*** \" else \"\") +\n            it.text + \" (\" + it.count + \")\" }\n        val builder = AlertDialog.Builder(context)\n        if (title != null) builder.setTitle(title)\n        if (viewerVote != null) builder.setMessage(R.string.story_quizzed)\n        builder.setPositiveButton(if (viewerVote == null) R.string.cancel else R.string.ok, null)\n        val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, choices.toTypedArray())\n        builder.setAdapter(adapter, onClickListener)\n        builder.show()\n    }\n\n    private fun createMentionDialog() {\n        val context = context ?: return\n        val adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, storiesViewModel.getMentionTexts())\n        val builder = AlertDialog.Builder(context)\n            .setPositiveButton(R.string.ok, null)\n            .setAdapter(adapter, { _, w ->\n                val data = storiesViewModel.getMention(w)\n                if (data != null) openProfile(Pair(data.second, data.third))\n            })\n        builder.show()\n    }\n\n    private fun createSliderDialog() {\n        val slider = storiesViewModel.getSlider().value ?: return\n        val context = context ?: return\n        val percentage: NumberFormat = NumberFormat.getPercentInstance()\n        percentage.maximumFractionDigits = 2\n        val sliderView = LinearLayout(context)\n        sliderView.layoutParams = LinearLayout.LayoutParams(\n            LinearLayout.LayoutParams.MATCH_PARENT,\n            LinearLayout.LayoutParams.WRAP_CONTENT\n        )\n        sliderView.orientation = LinearLayout.VERTICAL\n        val tv = TextView(context)\n        tv.gravity = Gravity.CENTER_HORIZONTAL\n        val input = SeekBar(context)\n        val avg: Double = slider.sliderVoteAverage ?: 0.5\n        input.progress = (avg * 100).toInt()\n        var onClickListener: OnClickListener? = null\n\n        if (slider.viewerVote == null && slider.viewerCanVote == true) {\n            input.isEnabled = true\n            input.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {\n                override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {\n                    sliderValue = progress / 100.0\n                    tv.text = percentage.format(sliderValue)\n                }\n\n                override fun onStartTrackingTouch(seekBar: SeekBar) {}\n                override fun onStopTrackingTouch(seekBar: SeekBar) {}\n            })\n            onClickListener = OnClickListener { _, _ -> storiesViewModel.answerSlider(sliderValue) }\n        }\n        else {\n            input.isEnabled = false\n            tv.text = getString(R.string.slider_answer, percentage.format(slider.viewerVote))\n        }\n        sliderView.addView(input)\n        sliderView.addView(tv)\n        val builder = AlertDialog.Builder(context)\n            .setTitle(if (slider.question.isNullOrEmpty()) slider.emoji else slider.question)\n            .setMessage(\n                resources.getQuantityString(R.plurals.slider_info,\n                slider.sliderVoteCount ?: 0,\n                slider.sliderVoteCount ?: 0,\n                percentage.format(avg)))\n            .setView(sliderView)\n            .setPositiveButton(R.string.ok, onClickListener)\n\n        builder.show()\n    }\n\n    private fun createReplyDialog(question: String?) {\n        val context = context ?: return\n        val input = TextInputEditText(context)\n        input.setHint(R.string.reply_hint)\n        val builder = AlertDialog.Builder(context)\n            .setTitle(question ?: context.getString(R.string.reply_story))\n            .setView(input)\n        val onClickListener = OnClickListener{ _, _ ->\n            val result =\n                if (question != null) storiesViewModel.answerQuestion(input.text.toString())\n                else storiesViewModel.reply(input.text.toString())\n            if (result == null) {\n                Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT)\n                    .show()\n            }\n            else result.observe(viewLifecycleOwner, {\n                when (it.status) {\n                    Resource.Status.SUCCESS -> {\n                        Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT)\n                            .show()\n                    }\n                    Resource.Status.ERROR -> {\n                        Toast.makeText(context, \"Error: \" + it.message, Toast.LENGTH_SHORT)\n                            .show()\n                    }\n                    Resource.Status.LOADING -> {\n                        Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show()\n                    }\n                }\n            })\n        }\n        builder.setPositiveButton(R.string.confirm, onClickListener)\n        builder.show()\n    }\n\n    private fun shareStoryViaDm() {\n        val story = storiesViewModel.getCurrentStory().value ?: return\n        val context = context\n        if (story.user?.isPrivate == true && context != null) {\n            Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show()\n        }\n        val actionBar = fragmentActivity.supportActionBar\n        if (actionBar != null) actionBar.subtitle = null\n        val actionGlobalUserSearch = StoryViewerFragmentDirections.actionToUserSearch().apply {\n            title = getString(R.string.share)\n            actionLabel = getString(R.string.send)\n            showGroups = true\n            multiple = true\n            searchMode = UserSearchMode.RAVEN\n        }\n        try {\n            val navController = NavHostFragment.findNavController(this@StoryViewerFragment)\n            navController.navigate(actionGlobalUserSearch)\n        } catch (e: Exception) {\n            Log.e(TAG, \"shareStoryViaDm: \", e)\n        }\n    }\n\n    private fun showStickerMenu() {\n        val data = storiesViewModel.getOptions().value\n        if (data == null) return\n        val themeWrapper = ContextThemeWrapper(context, R.style.popupMenuStyle)\n        val popupMenu = PopupMenu(themeWrapper, binding.stickers)\n        val menu = popupMenu.menu\n        data.first.map {\n            if (it.second != 0) menu.add(0, it.first, 0, it.second)\n            if (it.first == R.id.swipeUp) menu.add(0, R.id.swipeUp, 0, data.second)\n            if (it.first == R.id.spotify) menu.add(0, R.id.spotify, 0, data.third)\n        }\n        popupMenu.setOnMenuItemClickListener { item: MenuItem ->\n            val itemId = item.itemId\n            if (itemId == R.id.spotify) openExternalLink(storiesViewModel.getAppAttribution())\n            else if (itemId == R.id.swipeUp) openExternalLink(storiesViewModel.getSwipeUp())\n            else if (itemId == R.id.mentions) createMentionDialog()\n            else if (itemId == R.id.slider) createSliderDialog()\n            else if (itemId == R.id.question) {\n                val question = storiesViewModel.getQuestion().value\n                if (question != null) createReplyDialog(question.question)\n            }\n            else if (itemId == R.id.quiz) {\n                val quiz = storiesViewModel.getQuiz().value\n                if (quiz != null) createChoiceDialog(\n                    quiz.question,\n                    quiz.tallies,\n                    { _, w -> storiesViewModel.answerQuiz(w) },\n                    quiz.viewerAnswer,\n                    quiz.correctAnswer\n                )\n            }\n            else if (itemId == R.id.poll) {\n                val poll = storiesViewModel.getPoll().value\n                if (poll != null) createChoiceDialog(\n                    poll.question,\n                    poll.tallies,\n                    { _, w -> storiesViewModel.answerPoll(w) },\n                    poll.viewerVote,\n                    null\n                )\n            }\n            else if (itemId == R.id.viewStoryPost) {\n                storiesViewModel.getLinkedPost().observe(viewLifecycleOwner, {\n                    if (it == null) Toast.makeText(context, \"Error: LiveData is null\", Toast.LENGTH_SHORT).show()\n                    else when (it.status) {\n                        Resource.Status.SUCCESS -> {\n                            if (it.data != null) {\n                                val actionBar = fragmentActivity.supportActionBar\n                                if (actionBar != null) {\n                                    actionBar.title = null\n                                    actionBar.subtitle = null\n                                }\n                                val navController =\n                                    NavHostFragment.findNavController(this@StoryViewerFragment)\n                                val bundle = Bundle()\n                                bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, it.data)\n                                try {\n                                    navController.navigate(StoryViewerFragmentDirections.actionToPost(it.data, 0))\n                                } catch (e: Exception) {\n                                    Log.e(TAG, \"openPostDialog: \", e)\n                                }\n                            }\n                        }\n                        Resource.Status.ERROR -> {\n                            Toast.makeText(context, \"Error: \" + it.message, Toast.LENGTH_SHORT)\n                                .show()\n                        }\n                        Resource.Status.LOADING -> {\n                            Toast.makeText(context, R.string.opening_post, Toast.LENGTH_SHORT)\n                                .show()\n                        }\n                    }\n                })\n            }\n            false\n        }\n        popupMenu.show()\n    }\n\n    private fun openExternalLink(url: String?) {\n        val context = context ?: return\n        if (url == null) return\n        AlertDialog.Builder(context)\n            .setTitle(R.string.swipe_up_confirmation)\n            .setMessage(url).setPositiveButton(R.string.yes, { _, _ -> Utils.openURL(context, url) })\n            .setNegativeButton(R.string.no, null)\n            .show()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/UserSearchFragment.kt",
    "content": "package awais.instagrabber.fragments\n\nimport android.os.Bundle\nimport android.view.KeyEvent\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.ViewGroup.OnHierarchyChangeListener\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport androidx.navigation.NavController\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.transition.TransitionManager\nimport awais.instagrabber.activities.MainActivity\nimport awais.instagrabber.adapters.UserSearchResultsAdapter\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter\nimport awais.instagrabber.databinding.FragmentUserSearchBinding\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.trimAll\nimport awais.instagrabber.utils.measure\nimport awais.instagrabber.viewmodels.UserSearchViewModel\nimport com.google.android.material.chip.Chip\nimport com.google.android.material.snackbar.Snackbar\n\nclass UserSearchFragment : Fragment() {\n\n    private lateinit var binding: FragmentUserSearchBinding\n\n    private var resultsAdapter: UserSearchResultsAdapter? = null\n    private var paddingOffset = 0\n    private var actionLabel: String? = null\n    private var title: String? = null\n    private var multiple = false\n\n    private val viewModel: UserSearchViewModel by viewModels()\n    private val windowWidth = Utils.displayMetrics.widthPixels\n    private val minInputWidth = Utils.convertDpToPx(50f)\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {\n        binding = FragmentUserSearchBinding.inflate(inflater, container, false)\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        paddingOffset = with(binding) {\n            search.paddingStart + search.paddingEnd + group.paddingStart + group.paddingEnd + group.chipSpacingHorizontal\n        }\n        init()\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        viewModel.cleanup()\n    }\n\n    private fun init() {\n        val arguments = arguments\n        if (arguments != null) {\n            val fragmentArgs = UserSearchFragmentArgs.fromBundle(arguments)\n            actionLabel = fragmentArgs.actionLabel\n            title = fragmentArgs.title\n            multiple = fragmentArgs.multiple\n            viewModel.setHideThreadIds(fragmentArgs.hideThreadIds)\n            viewModel.setHideUserIds(fragmentArgs.hideUserIds)\n            viewModel.setSearchMode(fragmentArgs.searchMode)\n            viewModel.setShowGroups(fragmentArgs.showGroups)\n        }\n        setupTitles()\n        setupInput()\n        setupResults()\n        setupObservers()\n        // show cached results\n        viewModel.showCachedResults()\n    }\n\n    private fun setupTitles() {\n        if (!actionLabel.isNullOrBlank()) {\n            binding.done.text = actionLabel\n        }\n        if (title.isNullOrBlank()) return\n        (activity as MainActivity?)?.supportActionBar?.title = title\n    }\n\n    private fun setupResults() {\n        val context = context ?: return\n        binding.results.layoutManager = LinearLayoutManager(context)\n        resultsAdapter = UserSearchResultsAdapter(multiple) { _: Int, recipient: RankedRecipient, selected: Boolean ->\n            if (!multiple) {\n                val navController = NavHostFragment.findNavController(this)\n                if (!setResult(navController, recipient)) return@UserSearchResultsAdapter\n                navController.navigateUp()\n                return@UserSearchResultsAdapter\n            }\n            viewModel.setSelectedRecipient(recipient, !selected)\n            resultsAdapter?.setSelectedRecipient(recipient, !selected)\n            if (!selected) {\n                createChip(recipient)\n                return@UserSearchResultsAdapter\n            }\n            val chip = findChip(recipient) ?: return@UserSearchResultsAdapter\n            removeChipFromGroup(chip)\n        }\n        binding.results.adapter = resultsAdapter\n        binding.done.setOnClickListener {\n            val navController = NavHostFragment.findNavController(this)\n            if (!setResult(navController, viewModel.selectedRecipients)) return@setOnClickListener\n            navController.navigateUp()\n        }\n    }\n\n    private fun setResult(navController: NavController, rankedRecipient: RankedRecipient): Boolean {\n        navController.previousBackStackEntry?.savedStateHandle?.set(\"result\", rankedRecipient) ?: return false\n        return true\n    }\n\n    private fun setResult(navController: NavController, rankedRecipients: Set<RankedRecipient>): Boolean {\n        navController.previousBackStackEntry?.savedStateHandle?.set(\"result\", rankedRecipients) ?: return false\n        return true\n    }\n\n    private fun setupInput() {\n        binding.search.addTextChangedListener(object : TextWatcherAdapter() {\n            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {\n                viewModel.search(s.toString().trimAll())\n            }\n        })\n        binding.search.setOnKeyListener { _: View?, _: Int, event: KeyEvent? ->\n            if (event != null && event.action == KeyEvent.ACTION_DOWN && event.keyCode == KeyEvent.KEYCODE_DEL) {\n                val chip = lastChip ?: return@setOnKeyListener false\n                removeChip(chip)\n            }\n            false\n        }\n        binding.group.setOnHierarchyChangeListener(object : OnHierarchyChangeListener {\n            override fun onChildViewAdded(parent: View, child: View) {}\n            override fun onChildViewRemoved(parent: View, child: View) {\n                binding.group.post {\n                    TransitionManager.beginDelayedTransition(binding.root)\n                    calculateInputWidth(0)\n                }\n            }\n        })\n    }\n\n    private fun setupObservers() {\n        viewModel.recipients.observe(viewLifecycleOwner) {\n            if (it == null) return@observe\n            when (it.status) {\n                Resource.Status.SUCCESS -> if (it.data != null) {\n                    resultsAdapter?.submitList(it.data)\n                }\n                Resource.Status.ERROR -> {\n                    if (it.message != null) {\n                        Snackbar.make(binding.root, it.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (it.resId != 0) {\n                        Snackbar.make(binding.root, it.resId, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (it.data != null) {\n                        resultsAdapter?.submitList(it.data)\n                    }\n                }\n                Resource.Status.LOADING -> if (it.data != null) {\n                    resultsAdapter?.submitList(it.data)\n                }\n            }\n        }\n        viewModel.showAction().observe(viewLifecycleOwner) { binding.done.visibility = if (it) View.VISIBLE else View.GONE }\n    }\n\n    private fun createChip(recipient: RankedRecipient) {\n        val context = context ?: return\n        val chip = Chip(context).apply {\n            tag = recipient\n            text = getRecipientText(recipient)\n            isCloseIconVisible = true\n            setOnCloseIconClickListener { removeChip(this) }\n        }\n        binding.group.post {\n            val measure = measure(chip, binding.group)\n            TransitionManager.beginDelayedTransition(binding.root)\n            calculateInputWidth(if (measure.second != null) measure.second else 0)\n            binding.group.addView(chip, binding.group.childCount - 1)\n        }\n    }\n\n    private fun getRecipientText(recipient: RankedRecipient?): String? = when {\n        recipient == null -> null\n        recipient.user != null -> recipient.user.fullName\n        recipient.thread != null -> recipient.thread.threadTitle\n        else -> null\n    }\n\n    private fun removeChip(chip: View) {\n        val recipient = chip.tag as RankedRecipient\n        viewModel.setSelectedRecipient(recipient, false)\n        resultsAdapter?.setSelectedRecipient(recipient, false)\n        removeChipFromGroup(chip)\n    }\n\n    private fun findChip(recipient: RankedRecipient?): View? {\n        if (recipient == null || recipient.user == null && recipient.thread == null) return null\n        val isUser = recipient.user != null\n        val childCount = binding.group.childCount\n        if (childCount == 0) return null\n        for (i in childCount - 1 downTo 0) {\n            val child = binding.group.getChildAt(i) ?: continue\n            val tempTag = child.tag ?: continue\n            val tag = tempTag as RankedRecipient\n            if (isUser && tag.user == null || !isUser && tag.thread == null) continue\n            if (isUser && tag.user?.pk == recipient.user?.pk || !isUser && tag.thread?.threadId == recipient.thread?.threadId) {\n                return child\n            }\n        }\n        return null\n    }\n\n    private fun removeChipFromGroup(chip: View) {\n        binding.group.post {\n            TransitionManager.beginDelayedTransition(binding.root)\n            binding.group.removeView(chip)\n        }\n    }\n\n    private fun calculateInputWidth(newChipWidth: Int) {\n        var lastRight = lastChip?.right ?: 0\n        val remainingSpaceInRow = windowWidth - lastRight\n        if (remainingSpaceInRow < newChipWidth) {\n            // next chip will go to the next row, so assume no chips present\n            lastRight = 0\n        }\n        val newRight = lastRight + newChipWidth\n        val newInputWidth = windowWidth - newRight - paddingOffset\n        binding.search.layoutParams.width = if (newInputWidth < minInputWidth) windowWidth else newInputWidth\n        binding.search.requestLayout()\n    }\n\n    private val lastChip: View?\n        get() {\n            val childCount = binding.group.childCount\n            if (childCount == 0) return null\n            for (i in childCount - 1 downTo 0) {\n                val child = binding.group.getChildAt(i)\n                if (child is Chip) {\n                    return child\n                }\n            }\n            return null\n        }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/UserSearchMode.kt",
    "content": "package awais.instagrabber.fragments\n\nenum class UserSearchMode(val mode: String) {\n    USER_SEARCH(\"user_name\"),\n    RAVEN(\"raven\"),\n    RESHARE(\"reshare\");\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/comments/CommentsViewerFragment.java",
    "content": "package awais.instagrabber.fragments.comments;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.style.RelativeSizeSpan;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavController;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\n\nimport com.google.android.material.bottomsheet.BottomSheetBehavior;\nimport com.google.android.material.bottomsheet.BottomSheetDialog;\nimport com.google.android.material.bottomsheet.BottomSheetDialogFragment;\nimport com.google.android.material.snackbar.Snackbar;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.CommentsAdapter;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoader;\nimport awais.instagrabber.databinding.FragmentCommentsBinding;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.AppStateViewModel;\nimport awais.instagrabber.viewmodels.CommentsViewerViewModel;\n\npublic final class CommentsViewerFragment extends BottomSheetDialogFragment {\n    private static final String TAG = CommentsViewerFragment.class.getSimpleName();\n\n    private CommentsViewerViewModel viewModel;\n    private CommentsAdapter commentsAdapter;\n    private FragmentCommentsBinding binding;\n    private ConstraintLayout root;\n    private boolean shouldRefresh = true;\n    private AppStateViewModel appStateViewModel;\n    private boolean showingReplies;\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        final Dialog dialog = getDialog();\n        if (dialog == null) return;\n        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;\n        final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);\n        if (bottomSheetInternal == null) return;\n        bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;\n        bottomSheetInternal.requestLayout();\n        final BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheetInternal);\n        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);\n        behavior.setSkipCollapsed(true);\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final FragmentActivity activity = getActivity();\n        if (activity == null) return;\n        viewModel = new ViewModelProvider(this).get(CommentsViewerViewModel.class);\n        appStateViewModel = new ViewModelProvider(activity).get(AppStateViewModel.class);\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {\n        return new BottomSheetDialog(requireContext(), getTheme()) {\n            @Override\n            public void onBackPressed() {\n                if (showingReplies) {\n                    getChildFragmentManager().popBackStack();\n                    showingReplies = false;\n                    return;\n                }\n                super.onBackPressed();\n            }\n        };\n    }\n\n    @NonNull\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentCommentsBinding.inflate(getLayoutInflater());\n        binding.swipeRefreshLayout.setEnabled(false);\n        binding.swipeRefreshLayout.setNestedScrollingEnabled(false);\n        root = binding.getRoot();\n        appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), userResource -> {\n            if (userResource == null || userResource.status == Resource.Status.LOADING) return;\n            viewModel.setCurrentUser(userResource.data);\n            if (userResource.data == null) {\n                viewModel.fetchComments();\n                return;\n            }\n            viewModel.getCurrentUserId().observe(getViewLifecycleOwner(), new Observer<Long>() {\n                @Override\n                public void onChanged(final Long i) {\n                    if (i != 0L) {\n                        viewModel.fetchComments();\n                        viewModel.getCurrentUserId().removeObserver(this);\n                    }\n                }\n            });\n        });\n        if (getArguments() == null) return root;\n        final CommentsViewerFragmentArgs args = CommentsViewerFragmentArgs.fromBundle(getArguments());\n        viewModel.setPostDetails(args.getShortCode(), args.getPostId(), args.getPostUserId());\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        shouldRefresh = false;\n        init();\n    }\n\n    private void init() {\n        setupToolbar();\n        setupList();\n        setupObservers();\n    }\n\n    private void setupObservers() {\n        viewModel.getCurrentUserId().observe(getViewLifecycleOwner(), currentUserId -> {\n            long userId = 0;\n            if (currentUserId != null) {\n                userId = currentUserId;\n            }\n            setupAdapter(userId);\n            if (userId == 0) return;\n            Helper.setupCommentInput(binding.commentField, binding.commentText, false, text -> {\n                final LiveData<Resource<Object>> resourceLiveData = viewModel.comment(text, false);\n                resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {\n                    @Override\n                    public void onChanged(final Resource<Object> objectResource) {\n                        if (objectResource == null) return;\n                        final Context context = getContext();\n                        if (context == null) return;\n                        Helper.handleCommentResource(\n                                context,\n                                objectResource.status,\n                                objectResource.message,\n                                resourceLiveData,\n                                this,\n                                binding.commentField,\n                                binding.commentText,\n                                binding.comments);\n                    }\n                });\n                return null;\n            });\n        });\n        viewModel.getRootList().observe(getViewLifecycleOwner(), listResource -> {\n            if (listResource == null) return;\n            switch (listResource.status) {\n                case SUCCESS:\n                    binding.swipeRefreshLayout.setRefreshing(false);\n                    if (commentsAdapter != null) {\n                        commentsAdapter.submitList(listResource.data);\n                    }\n                    break;\n                case ERROR:\n                    binding.swipeRefreshLayout.setRefreshing(false);\n                    if (!TextUtils.isEmpty(listResource.message)) {\n                        Snackbar.make(binding.getRoot(), listResource.message, Snackbar.LENGTH_LONG).show();\n                    }\n                    break;\n                case LOADING:\n                    binding.swipeRefreshLayout.setRefreshing(true);\n                    break;\n            }\n        });\n        viewModel.getRootCommentsCount().observe(getViewLifecycleOwner(), count -> {\n            if (count == null || count == 0) {\n                binding.toolbar.setTitle(R.string.title_comments);\n                return;\n            }\n            final String titleComments = getString(R.string.title_comments);\n            final String countString = String.valueOf(count);\n            final SpannableString titleWithCount = new SpannableString(String.format(\"%s   %s\", titleComments, countString));\n            titleWithCount.setSpan(new RelativeSizeSpan(0.8f),\n                                   titleWithCount.length() - countString.length(),\n                                   titleWithCount.length(),\n                                   Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n            binding.toolbar.setTitle(titleWithCount);\n        });\n    }\n\n    private void setupToolbar() {\n        binding.toolbar.setTitle(R.string.title_comments);\n    }\n\n    private void setupAdapter(final long currentUserId) {\n        final Context context = getContext();\n        if (context == null) return;\n        commentsAdapter = new CommentsAdapter(currentUserId, false, Helper.getCommentCallback(\n                context,\n                getViewLifecycleOwner(),\n                getNavController(),\n                viewModel,\n                (comment, focusInput) -> {\n                    if (comment == null) return null;\n                    final boolean disableTransition = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS);\n                    final RepliesFragment repliesFragment = RepliesFragment.newInstance(comment, focusInput != null && focusInput);\n                    final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();\n                    if (!disableTransition) {\n                        transaction.setCustomAnimations(R.anim.slide_left, R.anim.slide_right, 0, R.anim.slide_right);\n                    }\n                    transaction.add(R.id.replies_container_view, repliesFragment)\n                               .addToBackStack(RepliesFragment.TAG)\n                               .commit();\n                    showingReplies = true;\n                    return null;\n                }));\n        final Resource<List<Comment>> listResource = viewModel.getRootList().getValue();\n        binding.comments.setAdapter(commentsAdapter);\n        commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList());\n    }\n\n    private void setupList() {\n        final Context context = getContext();\n        if (context == null) return;\n        final LinearLayoutManager layoutManager = new LinearLayoutManager(context);\n        final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> viewModel.fetchComments());\n        Helper.setupList(context, binding.comments, layoutManager, lazyLoader);\n    }\n\n    @Nullable\n    private NavController getNavController() {\n        NavController navController = null;\n        try {\n            navController = NavHostFragment.findNavController(this);\n        } catch (IllegalStateException e) {\n            Log.e(TAG, \"navigateToProfile\", e);\n        }\n        return navController;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/comments/Helper.java",
    "content": "package awais.instagrabber.fragments.comments;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.text.Editable;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.lifecycle.LifecycleOwner;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.recyclerview.widget.DividerItemDecoration;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder;\nimport com.google.android.material.internal.CheckableImageButton;\nimport com.google.android.material.textfield.TextInputEditText;\nimport com.google.android.material.textfield.TextInputLayout;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.CommentsAdapter.CommentCallback;\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter;\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.CommentsViewerViewModel;\nimport awais.instagrabber.webservices.ServiceCallback;\n\npublic final class Helper {\n    private static final String TAG = Helper.class.getSimpleName();\n\n    public static void setupList(@NonNull final Context context,\n                                 @NonNull final RecyclerView list,\n                                 @NonNull final RecyclerView.LayoutManager layoutManager,\n                                 @NonNull final RecyclerView.OnScrollListener lazyLoader) {\n        list.setLayoutManager(layoutManager);\n        final DividerItemDecoration itemDecoration = new DividerItemDecoration(context, LinearLayoutManager.VERTICAL);\n        final Drawable drawable = ContextCompat.getDrawable(context, R.drawable.pref_list_divider_material);\n        if (drawable != null) {\n            itemDecoration.setDrawable(drawable);\n        }\n        list.addItemDecoration(itemDecoration);\n        list.addOnScrollListener(lazyLoader);\n    }\n\n    @NonNull\n    public static CommentCallback getCommentCallback(@NonNull final Context context,\n                                                     final LifecycleOwner lifecycleOwner,\n                                                     final NavController navController,\n                                                     @NonNull final CommentsViewerViewModel viewModel,\n                                                     final BiFunction<Comment, Boolean, Void> onRepliesClick) {\n        return new CommentCallback() {\n            @Override\n            public void onClick(final Comment comment) {\n                // onCommentClick(comment);\n                if (onRepliesClick == null) return;\n                onRepliesClick.apply(comment, false);\n            }\n\n            @Override\n            public void onHashtagClick(final String hashtag) {\n                try {\n                    if (navController == null) return;\n                    navController.navigate(CommentsViewerFragmentDirections.actionToHashtag(hashtag));\n                } catch (Exception e) {\n                    Log.e(TAG, \"onHashtagClick: \", e);\n                }\n            }\n\n            @Override\n            public void onMentionClick(final String mention) {\n                openProfile(navController, mention);\n            }\n\n            @Override\n            public void onURLClick(final String url) {\n                Utils.openURL(context, url);\n            }\n\n            @Override\n            public void onEmailClick(final String emailAddress) {\n                Utils.openEmailAddress(context, emailAddress);\n            }\n\n            @Override\n            public void onLikeClick(final Comment comment, final boolean liked, final boolean isReply) {\n                if (comment == null) return;\n                final LiveData<Resource<Object>> resourceLiveData = viewModel.likeComment(comment, liked, isReply);\n                resourceLiveData.observe(lifecycleOwner, new Observer<Resource<Object>>() {\n                    @Override\n                    public void onChanged(final Resource<Object> objectResource) {\n                        if (objectResource == null) return;\n                        switch (objectResource.status) {\n                            case SUCCESS:\n                                resourceLiveData.removeObserver(this);\n                                break;\n                            case LOADING:\n                                break;\n                            case ERROR:\n                                if (objectResource.message != null) {\n                                    Toast.makeText(context, objectResource.message, Toast.LENGTH_LONG).show();\n                                }\n                                resourceLiveData.removeObserver(this);\n                        }\n                    }\n                });\n            }\n\n            @Override\n            public void onRepliesClick(final Comment comment) {\n                // viewModel.showReplies(comment);\n                if (onRepliesClick == null) return;\n                onRepliesClick.apply(comment, true);\n            }\n\n            @Override\n            public void onViewLikes(final Comment comment) {\n                try {\n                    if (navController == null) return;\n                    final NavDirections actionToLikes = CommentsViewerFragmentDirections.actionToLikes(comment.getPk(), true);\n                    navController.navigate(actionToLikes);\n                } catch (Exception e) {\n                    Log.e(TAG, \"onViewLikes: \", e);\n                }\n            }\n\n            @Override\n            public void onTranslate(final Comment comment) {\n                if (comment == null) return;\n                viewModel.translate(comment, new ServiceCallback<String>() {\n                    @Override\n                    public void onSuccess(final String result) {\n                        if (TextUtils.isEmpty(result)) {\n                            Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();\n                            return;\n                        }\n                        final String username = comment.getUser().getUsername();\n                        new MaterialAlertDialogBuilder(context)\n                                .setTitle(username)\n                                .setMessage(result)\n                                .setPositiveButton(R.string.ok, null)\n                                .show();\n                    }\n\n                    @Override\n                    public void onFailure(final Throwable t) {\n                        Log.e(TAG, \"Error translating comment\", t);\n                        Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();\n                    }\n                });\n            }\n\n            @Override\n            public void onDelete(final Comment comment, final boolean isReply) {\n                if (comment == null) return;\n                final LiveData<Resource<Object>> resourceLiveData = viewModel.deleteComment(comment, isReply);\n                resourceLiveData.observe(lifecycleOwner, new Observer<Resource<Object>>() {\n                    @Override\n                    public void onChanged(final Resource<Object> objectResource) {\n                        if (objectResource == null) return;\n                        switch (objectResource.status) {\n                            case SUCCESS:\n                                resourceLiveData.removeObserver(this);\n                                break;\n                            case ERROR:\n                                if (objectResource.message != null) {\n                                    Toast.makeText(context, objectResource.message, Toast.LENGTH_LONG).show();\n                                }\n                                resourceLiveData.removeObserver(this);\n                                break;\n                            case LOADING:\n                                break;\n                        }\n                    }\n                });\n            }\n        };\n    }\n\n    private static void openProfile(final NavController navController,\n                                    @NonNull final String username) {\n        try {\n            if (navController == null) return;\n            final NavDirections action = CommentsViewerFragmentDirections.actionToProfile().setUsername(username);\n            navController.navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"openProfile: \", e);\n        }\n    }\n\n    public static void setupCommentInput(@NonNull final TextInputLayout commentField,\n                                         @NonNull final TextInputEditText commentText,\n                                         final boolean isReplyFragment,\n                                         @NonNull final Function<String, Void> commentFunction) {\n        // commentField.setStartIconVisible(false);\n        commentField.setVisibility(View.VISIBLE);\n        commentField.setEndIconVisible(false);\n        if (isReplyFragment) {\n            commentField.setHint(R.string.reply_hint);\n        }\n        commentText.addTextChangedListener(new TextWatcherAdapter() {\n            @Override\n            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n                final boolean isEmpty = TextUtils.isEmpty(s);\n                commentField.setStartIconVisible(!isEmpty);\n                commentField.setEndIconVisible(!isEmpty);\n                commentField.setCounterEnabled(s != null && s.length() > 2000); // show the counter when user approaches the limit\n            }\n        });\n        // commentField.setStartIconOnClickListener(v -> {\n        //     // commentsAdapter.clearSelection();\n        //     commentText.setText(\"\");\n        // });\n        commentField.setEndIconOnClickListener(v -> {\n            final Editable text = commentText.getText();\n            if (TextUtils.isEmpty(text)) return;\n            commentFunction.apply(text.toString().trim());\n        });\n    }\n\n    public static void handleCommentResource(@NonNull final Context context,\n                                             @NonNull final Resource.Status status,\n                                             final String message,\n                                             @NonNull final LiveData<Resource<Object>> resourceLiveData,\n                                             @NonNull final Observer<Resource<Object>> observer,\n                                             @NonNull final TextInputLayout commentField,\n                                             @NonNull final TextInputEditText commentText,\n                                             @NonNull final RecyclerView comments) {\n        CheckableImageButton endIcon = null;\n        try {\n            endIcon = (CheckableImageButton) commentField.findViewById(com.google.android.material.R.id.text_input_end_icon);\n        } catch (Exception e) {\n            Log.e(TAG, \"setupObservers: \", e);\n        }\n        CheckableImageButton startIcon = null;\n        try {\n            startIcon = (CheckableImageButton) commentField.findViewById(com.google.android.material.R.id.text_input_start_icon);\n        } catch (Exception e) {\n            Log.e(TAG, \"setupObservers: \", e);\n        }\n        switch (status) {\n            case SUCCESS:\n                resourceLiveData.removeObserver(observer);\n                comments.postDelayed(() -> comments.scrollToPosition(0), 500);\n                if (startIcon != null) {\n                    startIcon.setEnabled(true);\n                }\n                if (endIcon != null) {\n                    endIcon.setEnabled(true);\n                }\n                commentText.setText(\"\");\n                break;\n            case LOADING:\n                commentText.setEnabled(false);\n                if (startIcon != null) {\n                    startIcon.setEnabled(false);\n                }\n                if (endIcon != null) {\n                    endIcon.setEnabled(false);\n                }\n                break;\n            case ERROR:\n                if (message != null && context != null) {\n                    Toast.makeText(context, message, Toast.LENGTH_LONG).show();\n                }\n                if (startIcon != null) {\n                    startIcon.setEnabled(true);\n                }\n                if (endIcon != null) {\n                    endIcon.setEnabled(true);\n                }\n                resourceLiveData.removeObserver(observer);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/comments/RepliesFragment.java",
    "content": "package awais.instagrabber.fragments.comments;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavController;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\n\nimport com.google.android.material.snackbar.Snackbar;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.adapters.CommentsAdapter;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoader;\nimport awais.instagrabber.databinding.FragmentCommentsBinding;\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.CommentsViewerViewModel;\n\npublic class RepliesFragment extends Fragment {\n    public static final String TAG = RepliesFragment.class.getSimpleName();\n    private static final String ARG_PARENT = \"parent\";\n    private static final String ARG_FOCUS_INPUT = \"focus\";\n\n    private FragmentCommentsBinding binding;\n    private CommentsViewerViewModel viewModel;\n    private CommentsAdapter commentsAdapter;\n\n    @NonNull\n    public static RepliesFragment newInstance(@NonNull final Comment parent,\n                                              final boolean focusInput) {\n        final Bundle args = new Bundle();\n        args.putSerializable(ARG_PARENT, parent);\n        args.putBoolean(ARG_FOCUS_INPUT, focusInput);\n        final RepliesFragment fragment = new RepliesFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final Fragment parentFragment = getParentFragment();\n        if (parentFragment == null) return;\n        viewModel = new ViewModelProvider(parentFragment).get(CommentsViewerViewModel.class);\n        final Bundle bundle = getArguments();\n        if (bundle == null) return;\n        final Serializable serializable = bundle.getSerializable(ARG_PARENT);\n        if (!(serializable instanceof Comment)) return;\n        viewModel.showReplies((Comment) serializable);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = FragmentCommentsBinding.inflate(inflater, container, false);\n        binding.swipeRefreshLayout.setEnabled(false);\n        binding.swipeRefreshLayout.setNestedScrollingEnabled(false);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        setupToolbar();\n    }\n\n    @Override\n    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {\n        if (!enter) {\n            return super.onCreateAnimation(transit, false, nextAnim);\n        }\n        if (nextAnim == 0) {\n            setupList();\n            setupObservers();\n            return super.onCreateAnimation(transit, true, nextAnim);\n        }\n        final Animation animation = AnimationUtils.loadAnimation(getContext(), nextAnim);\n        animation.setAnimationListener(new Animation.AnimationListener() {\n            @Override\n            public void onAnimationStart(Animation animation) {}\n\n            @Override\n            public void onAnimationEnd(Animation animation) {\n                setupList();\n                setupObservers();\n            }\n\n            @Override\n            public void onAnimationRepeat(Animation animation) {}\n        });\n        return animation;\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        binding = null;\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (viewModel != null) {\n            viewModel.clearReplies();\n        }\n    }\n\n    private void setupObservers() {\n        if (viewModel == null) return;\n        viewModel.getCurrentUserId().observe(getViewLifecycleOwner(), currentUserId -> {\n            long userId = 0;\n            if (currentUserId != null) {\n                userId = currentUserId;\n            }\n            setupAdapter(userId);\n            if (userId == 0) return;\n            Helper.setupCommentInput(binding.commentField, binding.commentText, true, text -> {\n                final LiveData<Resource<Object>> resourceLiveData = viewModel.comment(text, true);\n                resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {\n                    @Override\n                    public void onChanged(final Resource<Object> objectResource) {\n                        if (objectResource == null) return;\n                        final Context context = getContext();\n                        if (context == null) return;\n                        Helper.handleCommentResource(context,\n                                                     objectResource.status,\n                                                     objectResource.message,\n                                                     resourceLiveData,\n                                                     this,\n                                                     binding.commentField,\n                                                     binding.commentText,\n                                                     binding.comments);\n                    }\n                });\n                return null;\n            });\n            final Bundle bundle = getArguments();\n            if (bundle == null) return;\n            final boolean focusInput = bundle.getBoolean(ARG_FOCUS_INPUT);\n            if (focusInput && viewModel.getRepliesParent() != null) {\n                viewModel.getRepliesParent().getUser();\n                binding.commentText.setText(String.format(\"@%s \", viewModel.getRepliesParent().getUser().getUsername()));\n                Utils.showKeyboard(binding.commentText);\n            }\n        });\n        viewModel.getReplyList().observe(getViewLifecycleOwner(), listResource -> {\n            if (listResource == null) return;\n            switch (listResource.status) {\n                case SUCCESS:\n                    binding.swipeRefreshLayout.setRefreshing(false);\n                    if (commentsAdapter != null) {\n                        commentsAdapter.submitList(listResource.data);\n                    }\n                    break;\n                case ERROR:\n                    binding.swipeRefreshLayout.setRefreshing(false);\n                    final String message = listResource.message;\n                    if (!TextUtils.isEmpty(message)) {\n                        Snackbar.make(binding.getRoot(), message, Snackbar.LENGTH_LONG).show();\n                    }\n                    break;\n                case LOADING:\n                    binding.swipeRefreshLayout.setRefreshing(true);\n                    break;\n            }\n        });\n    }\n\n    private void setupToolbar() {\n        binding.toolbar.setTitle(R.string.title_replies);\n        binding.toolbar.setNavigationIcon(R.drawable.ic_round_arrow_back_24);\n        binding.toolbar.setNavigationOnClickListener(v -> {\n            final FragmentManager fragmentManager = getParentFragmentManager();\n            fragmentManager.popBackStack();\n        });\n    }\n\n    private void setupAdapter(final long currentUserId) {\n        if (viewModel == null) return;\n        final Context context = getContext();\n        if (context == null) return;\n        commentsAdapter = new CommentsAdapter(\n                currentUserId,\n                true,\n                Helper.getCommentCallback(\n                        context,\n                        getViewLifecycleOwner(),\n                        getNavController(),\n                        viewModel,\n                        (comment, focusInput) -> {\n                            viewModel.setReplyTo(comment);\n                            binding.commentText.setText(String.format(\"@%s \", comment.getUser().getUsername()));\n                            if (focusInput) Utils.showKeyboard(binding.commentText);\n                            return null;\n                        }\n                )\n        );\n        binding.comments.setAdapter(commentsAdapter);\n        final Resource<List<Comment>> listResource = viewModel.getReplyList().getValue();\n        commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList());\n    }\n\n    private void setupList() {\n        final Context context = getContext();\n        if (context == null) return;\n        final LinearLayoutManager layoutManager = new LinearLayoutManager(context);\n        final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {\n            if (viewModel != null) viewModel.fetchReplies();\n        });\n        Helper.setupList(context, binding.comments, layoutManager, lazyLoader);\n    }\n\n    @Nullable\n    private NavController getNavController() {\n        NavController navController = null;\n        try {\n            navController = NavHostFragment.findNavController(this);\n        } catch (IllegalStateException e) {\n            Log.e(TAG, \"navigateToProfile\", e);\n        }\n        return navController;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.kt",
    "content": "package awais.instagrabber.fragments.directmessages\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.util.Log\nimport android.view.*\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.activityViewModels\nimport androidx.navigation.fragment.findNavController\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener\nimport awais.instagrabber.R\nimport awais.instagrabber.activities.MainActivity\nimport awais.instagrabber.adapters.DirectMessageInboxAdapter\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge\nimport awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.directmessages.DirectInbox\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.viewmodels.DirectInboxViewModel\nimport com.google.android.material.badge.BadgeDrawable\nimport com.google.android.material.badge.BadgeUtils\nimport com.google.android.material.internal.ToolbarUtils\nimport com.google.android.material.snackbar.Snackbar\n\nclass DirectMessageInboxFragment : Fragment(), OnRefreshListener {\n    private val viewModel: DirectInboxViewModel by activityViewModels()\n\n    private lateinit var fragmentActivity: MainActivity\n    private lateinit var binding: FragmentDirectMessagesInboxBinding\n    private lateinit var lazyLoader: RecyclerLazyLoaderAtEdge\n\n    private var scrollToTop = false\n    private var navigating = false\n\n    private var pendingRequestsMenuItem: MenuItem? = null\n    private var pendingRequestTotalBadgeDrawable: BadgeDrawable? = null\n    private var isPendingRequestTotalBadgeAttached = false\n    private var inboxAdapter: DirectMessageInboxAdapter? = null\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        fragmentActivity = requireActivity() as MainActivity\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?,\n    ): View {\n        binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false)\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        init()\n    }\n\n    override fun onRefresh() {\n        lazyLoader.resetState()\n        scrollToTop = true\n        viewModel.refresh()\n    }\n\n    @SuppressLint(\"UnsafeExperimentalUsageError\", \"UnsafeOptInUsageError\", \"RestrictedApi\")\n    override fun onPause() {\n        super.onPause()\n        isPendingRequestTotalBadgeAttached = false\n        pendingRequestsMenuItem?.let {\n            val menuItemView = ToolbarUtils.getActionMenuItemView(fragmentActivity.getToolbar(), it.itemId)\n            if (menuItemView != null) {\n                BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), it.itemId)\n                pendingRequestTotalBadgeDrawable = null\n            }\n        }\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        inflater.inflate(R.menu.dm_inbox_menu, menu)\n        pendingRequestsMenuItem = menu.findItem(R.id.pending_requests)\n        pendingRequestsMenuItem?.isVisible = isPendingRequestTotalBadgeAttached\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        if (item.itemId == R.id.pending_requests) {\n            try {\n                val directions = DirectMessageInboxFragmentDirections.actionToPendingInbox()\n                findNavController().navigate(directions)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onOptionsItemSelected: \", e)\n            }\n            return true\n        }\n        return super.onOptionsItemSelected(item)\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        init()\n    }\n\n    private fun setupObservers() {\n        viewModel.threads.observe(viewLifecycleOwner, { list: List<DirectThread?> ->\n            inboxAdapter?.submitList(list) {\n                if (!scrollToTop) return@submitList\n                binding.inboxList.post { binding.inboxList.smoothScrollToPosition(0) }\n                scrollToTop = false\n            }\n        })\n        viewModel.inbox.observe(viewLifecycleOwner, { inboxResource: Resource<DirectInbox?>? ->\n            if (inboxResource == null) return@observe\n            when (inboxResource.status) {\n                Resource.Status.SUCCESS -> binding.swipeRefreshLayout.isRefreshing = false\n                Resource.Status.ERROR -> {\n                    if (inboxResource.message != null) {\n                        Snackbar.make(binding.root, inboxResource.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (inboxResource.resId != 0) {\n                        Snackbar.make(binding.root, inboxResource.resId, Snackbar.LENGTH_LONG).show()\n                    }\n                    binding.swipeRefreshLayout.isRefreshing = false\n                }\n                Resource.Status.LOADING -> binding.swipeRefreshLayout.isRefreshing = true\n            }\n        })\n        viewModel.pendingRequestsTotal.observe(viewLifecycleOwner, { count: Int? -> attachPendingRequestsBadge(count) })\n    }\n\n    @SuppressLint(\"UnsafeExperimentalUsageError\", \"UnsafeOptInUsageError\", \"RestrictedApi\")\n    private fun attachPendingRequestsBadge(count: Int?) {\n        val pendingRequestsMenuItem1 = pendingRequestsMenuItem\n        if (pendingRequestsMenuItem1 == null) {\n            val handler = Handler(Looper.getMainLooper())\n            handler.postDelayed({ attachPendingRequestsBadge(count) }, 500)\n            return\n        }\n        if (pendingRequestTotalBadgeDrawable == null) {\n            val context = context ?: return\n            pendingRequestTotalBadgeDrawable = BadgeDrawable.create(context)\n        }\n        if (count == null || count == 0) {\n            val menuItemView = ToolbarUtils.getActionMenuItemView(\n                fragmentActivity.getToolbar(),\n                pendingRequestsMenuItem1.itemId\n            )\n            if (menuItemView != null) {\n                BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem1.itemId)\n            }\n            isPendingRequestTotalBadgeAttached = false\n            pendingRequestTotalBadgeDrawable?.number = 0\n            pendingRequestsMenuItem1.isVisible = false\n            return\n        }\n        pendingRequestsMenuItem1.isVisible = true\n        if (pendingRequestTotalBadgeDrawable?.number == count) return\n        pendingRequestTotalBadgeDrawable?.number = count\n        if (!isPendingRequestTotalBadgeAttached) {\n            pendingRequestTotalBadgeDrawable?.let {\n                BadgeUtils.attachBadgeDrawable(it, fragmentActivity.getToolbar(), pendingRequestsMenuItem1.itemId)\n                isPendingRequestTotalBadgeAttached = true\n            }\n        }\n    }\n\n    private fun init() {\n        val context = context ?: return\n        setupObservers()\n        binding.swipeRefreshLayout.setOnRefreshListener(this)\n        binding.inboxList.setHasFixedSize(true)\n        binding.inboxList.setItemViewCacheSize(20)\n        val layoutManager = LinearLayoutManager(context)\n        binding.inboxList.layoutManager = layoutManager\n        inboxAdapter = DirectMessageInboxAdapter { thread ->\n            val threadId = thread.threadId\n            val threadTitle = thread.threadTitle\n            if (navigating || threadId.isNullOrBlank() || threadTitle.isNullOrBlank()) return@DirectMessageInboxAdapter\n            navigating = true\n            if (isAdded) {\n                try {\n                    val directions = DirectMessageInboxFragmentDirections.actionToThread(threadId, threadTitle)\n                    findNavController().navigate(directions)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"init: \", e)\n                }\n            }\n            navigating = false\n        }.also {\n            it.setHasStableIds(true)\n        }\n        binding.inboxList.adapter = inboxAdapter\n        lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() }.also {\n            binding.inboxList.addOnScrollListener(it)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.kt",
    "content": "package awais.instagrabber.fragments.directmessages\n\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.CompoundButton\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.activityViewModels\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.navigation.fragment.findNavController\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport awais.instagrabber.R\nimport awais.instagrabber.activities.MainActivity\nimport awais.instagrabber.adapters.DirectPendingUsersAdapter\nimport awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser\nimport awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback\nimport awais.instagrabber.adapters.DirectUsersAdapter\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter\nimport awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding\nimport awais.instagrabber.dialogs.ConfirmDialogFragment\nimport awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback\nimport awais.instagrabber.dialogs.MultiOptionDialogFragment\nimport awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback\nimport awais.instagrabber.fragments.UserSearchMode\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.viewmodels.AppStateViewModel\nimport awais.instagrabber.viewmodels.DirectSettingsViewModel\nimport awais.instagrabber.viewmodels.factories.DirectSettingsViewModelFactory\nimport com.google.android.material.snackbar.Snackbar\nimport java.util.*\n\nclass DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback {\n    private lateinit var viewModel: DirectSettingsViewModel\n    private lateinit var binding: FragmentDirectMessagesSettingsBinding\n\n    private var usersAdapter: DirectUsersAdapter? = null\n    private var isPendingRequestsSetupDone = false\n    private var pendingUsersAdapter: DirectPendingUsersAdapter? = null\n    private var approvalRequiredUsers: Set<User>? = null\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val arguments = arguments ?: return\n        val args = DirectMessageSettingsFragmentArgs.fromBundle(arguments)\n        val fragmentActivity = requireActivity() as MainActivity\n        val appStateViewModel: AppStateViewModel by activityViewModels()\n        val currentUser = appStateViewModel.currentUser?.data ?: return\n        val viewModelFactory = DirectSettingsViewModelFactory(\n            fragmentActivity.application,\n            args.threadId,\n            args.pending,\n            currentUser\n        )\n        viewModel = ViewModelProvider(this, viewModelFactory).get(DirectSettingsViewModel::class.java)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?,\n    ): View {\n        binding = FragmentDirectMessagesSettingsBinding.inflate(inflater, container, false)\n        // currentlyRunning = new DirectMessageInboxThreadFetcher(threadId, null, null, fetchListener).execute();\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        init()\n        setupObservers()\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        isPendingRequestsSetupDone = false\n    }\n\n    private fun setupObservers() {\n        viewModel.inputMode.observe(viewLifecycleOwner, { inputMode: Int? ->\n            if (inputMode == null || inputMode == 0) return@observe\n            if (inputMode == 1) {\n                binding.groupSettings.visibility = View.GONE\n                binding.pendingMembersGroup.visibility = View.GONE\n                binding.approvalRequired.visibility = View.GONE\n                binding.approvalRequiredLabel.visibility = View.GONE\n                binding.muteMessagesLabel.visibility = View.GONE\n                binding.muteMessages.visibility = View.GONE\n            }\n        })\n        // Need to observe, so that getValue is correct\n        viewModel.getUsers().observe(viewLifecycleOwner, { })\n        viewModel.getLeftUsers().observe(viewLifecycleOwner, { })\n        viewModel.getUsersAndLeftUsers().observe(viewLifecycleOwner, { usersAdapter?.submitUsers(it.first, it.second) })\n        viewModel.getTitle().observe(viewLifecycleOwner, { binding.titleEdit.setText(it) })\n        viewModel.getAdminUserIds().observe(viewLifecycleOwner, { usersAdapter?.setAdminUserIds(it) })\n        viewModel.isMuted().observe(viewLifecycleOwner, { binding.muteMessages.isChecked = it })\n        viewModel.isPending().observe(viewLifecycleOwner, { binding.muteMessages.visibility = if (it) View.GONE else View.VISIBLE })\n        viewModel.isViewerAdmin().observe(viewLifecycleOwner, { setApprovalRelatedUI(it) })\n        viewModel.getApprovalRequiredToJoin().observe(viewLifecycleOwner, { binding.approvalRequired.isChecked = it })\n        viewModel.getPendingRequests().observe(viewLifecycleOwner, { setPendingRequests(it) })\n        viewModel.isGroup().observe(viewLifecycleOwner, { isGroup: Boolean -> setupSettings(isGroup) })\n        val navController = NavHostFragment.findNavController(this)\n        val backStackEntry = navController.currentBackStackEntry\n        if (backStackEntry != null) {\n            val resultLiveData = backStackEntry.savedStateHandle.getLiveData<Any>(\"result\")\n            resultLiveData.observe(viewLifecycleOwner, { result: Any? ->\n                if (result == null) return@observe\n                if (result is RankedRecipient) {\n                    val user = getUser(result)\n                    // Log.d(TAG, \"result: \" + user);\n                    if (user != null) {\n                        addMembers(setOf(user))\n                    }\n                } else if (result is Set<*>) {\n                    try {\n                        @Suppress(\"UNCHECKED_CAST\") val recipients = result as Set<RankedRecipient>\n                        val users: Set<User> = recipients.asSequence()\n                            .filterNotNull()\n                            .map { getUser(it) }\n                            .filterNotNull()\n                            .toSet()\n                        // Log.d(TAG, \"result: \" + users);\n                        addMembers(users)\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"search users result: \", e)\n                        Snackbar.make(binding.root, e.message ?: \"\", Snackbar.LENGTH_LONG).show()\n                    }\n                }\n            })\n        }\n    }\n\n    private fun addMembers(users: Set<User>) {\n        val approvalRequired = viewModel.getApprovalRequiredToJoin().value\n        var isViewerAdmin = viewModel.isViewerAdmin().value\n        if (isViewerAdmin == null) {\n            isViewerAdmin = false\n        }\n        if (!isViewerAdmin && approvalRequired != null && approvalRequired) {\n            approvalRequiredUsers = users\n            val confirmDialogFragment = ConfirmDialogFragment.newInstance(\n                APPROVAL_REQUIRED_REQUEST_CODE,\n                R.string.admin_approval_required,\n                R.string.admin_approval_required_description,\n                R.string.ok,\n                R.string.cancel,\n                0\n            )\n            confirmDialogFragment.show(childFragmentManager, \"approval_required_dialog\")\n            return\n        }\n        val detailsChangeResourceLiveData = viewModel.addMembers(users)\n        observeDetailsChange(detailsChangeResourceLiveData)\n    }\n\n    private fun getUser(recipient: RankedRecipient): User? {\n        var user: User? = null\n        if (recipient.user != null) {\n            user = recipient.user\n        } else if (recipient.thread != null && !recipient.thread.isGroup) {\n            user = recipient.thread.users?.get(0)\n        }\n        return user\n    }\n\n    private fun init() {\n        // setupSettings();\n        setupMembers()\n    }\n\n    private fun setupSettings(isGroup: Boolean) {\n        binding.groupSettings.visibility = if (isGroup) View.VISIBLE else View.GONE\n        binding.muteMessagesLabel.setOnClickListener { binding.muteMessages.toggle() }\n        binding.muteMessages.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean ->\n            val resourceLiveData = if (isChecked) viewModel.mute() else viewModel.unmute()\n            handleSwitchChangeResource(resourceLiveData, buttonView)\n        }\n        if (!isGroup) return\n        binding.titleEdit.addTextChangedListener(object : TextWatcherAdapter() {\n            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {\n                if (s.toString().trim { it <= ' ' } == viewModel.getTitle().value) {\n                    binding.titleEditInputLayout.suffixText = null\n                    return\n                }\n                binding.titleEditInputLayout.suffixText = getString(R.string.save)\n            }\n        })\n        binding.titleEditInputLayout.suffixTextView.setOnClickListener {\n            val text = binding.titleEdit.text ?: return@setOnClickListener\n            val newTitle = text.toString().trim { it <= ' ' }\n            if (newTitle == viewModel.getTitle().value) return@setOnClickListener\n            observeDetailsChange(viewModel.updateTitle(newTitle))\n        }\n        binding.addMembers.setOnClickListener {\n            if (!isAdded) return@setOnClickListener\n            try {\n                val navController = findNavController()\n                if (navController.currentDestination?.id != R.id.directMessagesSettingsFragment) return@setOnClickListener\n                val users = viewModel.getUsers().value ?: return@setOnClickListener\n                val currentUserIds = users.asSequence().map(User::pk).sorted().toList().toLongArray()\n                val actionGlobalUserSearch = DirectMessageSettingsFragmentDirections.actionToUserSearch().apply {\n                    title = getString(R.string.add_members)\n                    actionLabel = getString(R.string.add)\n                    hideUserIds = currentUserIds\n                    searchMode = UserSearchMode.RAVEN\n                    multiple = true\n                }\n                navController.navigate(actionGlobalUserSearch)\n            } catch (e: Exception) {\n                Log.e(TAG, \"setupSettings: \", e)\n            }\n        }\n        binding.muteMentionsLabel.setOnClickListener { binding.muteMentions.toggle() }\n        binding.muteMentions.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean ->\n            val resourceLiveData = if (isChecked) viewModel.muteMentions() else viewModel.unmuteMentions()\n            handleSwitchChangeResource(resourceLiveData, buttonView)\n        }\n        binding.leave.setOnClickListener {\n            val confirmDialogFragment = ConfirmDialogFragment.newInstance(\n                LEAVE_THREAD_REQUEST_CODE,\n                R.string.dms_action_leave_question,\n                0,\n                R.string.yes,\n                R.string.no,\n                0\n            )\n            confirmDialogFragment.show(childFragmentManager, \"leave_thread_confirmation_dialog\")\n        }\n        var isViewerAdmin = viewModel.isViewerAdmin().value\n        if (isViewerAdmin == null) isViewerAdmin = false\n        if (isViewerAdmin) {\n            binding.end.visibility = View.VISIBLE\n            binding.end.setOnClickListener {\n                val confirmDialogFragment = ConfirmDialogFragment.newInstance(\n                    END_THREAD_REQUEST_CODE,\n                    R.string.dms_action_end_question,\n                    R.string.dms_action_end_description,\n                    R.string.yes,\n                    R.string.no,\n                    0\n                )\n                confirmDialogFragment.show(childFragmentManager, \"end_thread_confirmation_dialog\")\n            }\n        } else {\n            binding.end.visibility = View.GONE\n        }\n    }\n\n    private fun setApprovalRelatedUI(isViewerAdmin: Boolean) {\n        if (!isViewerAdmin) {\n            binding.pendingMembersGroup.visibility = View.GONE\n            binding.approvalRequired.visibility = View.GONE\n            binding.approvalRequiredLabel.visibility = View.GONE\n            return\n        }\n        binding.approvalRequired.visibility = View.VISIBLE\n        binding.approvalRequiredLabel.visibility = View.VISIBLE\n        binding.approvalRequiredLabel.setOnClickListener { binding.approvalRequired.toggle() }\n        binding.approvalRequired.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean ->\n            val resourceLiveData = if (isChecked) viewModel.approvalRequired() else viewModel.approvalNotRequired()\n            handleSwitchChangeResource(resourceLiveData, buttonView)\n        }\n    }\n\n    private fun handleSwitchChangeResource(resourceLiveData: LiveData<Resource<Any?>>, buttonView: CompoundButton) {\n        resourceLiveData.observe(viewLifecycleOwner, { resource: Resource<Any?>? ->\n            if (resource == null) return@observe\n            when (resource.status) {\n                Resource.Status.SUCCESS -> buttonView.isEnabled = true\n                Resource.Status.ERROR -> {\n                    buttonView.isEnabled = true\n                    buttonView.isChecked = !buttonView.isChecked\n                    if (resource.message != null) {\n                        Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (resource.resId != 0) {\n                        Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show()\n                    }\n                }\n                Resource.Status.LOADING -> buttonView.isEnabled = false\n            }\n        })\n    }\n\n    private fun setupMembers() {\n        val context = context ?: return\n        binding.users.layoutManager = LinearLayoutManager(context)\n        val inviter = viewModel.getInviter().value\n        usersAdapter = DirectUsersAdapter(\n            inviter?.pk ?: -1,\n            { _: Int, user: User, _: Boolean ->\n                if (user.username.isBlank() && !user.interopMessagingUserFbid.isNullOrBlank()) {\n                    Utils.openURL(context, \"https://facebook.com/\" + user.interopMessagingUserFbid)\n                    return@DirectUsersAdapter\n                }\n                if (user.username.isBlank()) return@DirectUsersAdapter\n                try {\n                    val directions = DirectMessageSettingsFragmentDirections.actionToProfile().apply { this.username = user.username }\n                    findNavController().navigate(directions)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"setupMembers: \", e)\n                }\n            },\n            { _: Int, user: User? ->\n                val options = viewModel.createUserOptions(user)\n                if (options.isEmpty()) return@DirectUsersAdapter true\n                val fragment = MultiOptionDialogFragment.newInstance(0, -1, options)\n                fragment.setSingleCallback(object : MultiOptionDialogSingleCallback<String?> {\n                    override fun onSelect(requestCode: Int, action: String?) {\n                        if (action == null) return\n                        val resourceLiveData = viewModel.doAction(user, action)\n                        if (resourceLiveData != null) {\n                            observeDetailsChange(resourceLiveData)\n                        }\n                    }\n\n                    override fun onCancel(requestCode: Int) {}\n                })\n                val fragmentManager = childFragmentManager\n                fragment.show(fragmentManager, \"actions\")\n                true\n            }\n        )\n        binding.users.adapter = usersAdapter\n    }\n\n    private fun setPendingRequests(requests: DirectThreadParticipantRequestsResponse?) {\n        val nullOrEmpty: Boolean = requests?.users?.isNullOrEmpty() ?: true\n        if (nullOrEmpty) {\n            binding.pendingMembersGroup.visibility = View.GONE\n            return\n        }\n        if (!isPendingRequestsSetupDone) {\n            val context = context ?: return\n            binding.pendingMembers.layoutManager = LinearLayoutManager(context)\n            pendingUsersAdapter = DirectPendingUsersAdapter(object : PendingUserCallback {\n                override fun onClick(position: Int, pendingUser: PendingUser) {\n                    try {\n                        val directions = DirectMessageSettingsFragmentDirections.actionToProfile().apply { this.username = pendingUser.user.username }\n                        findNavController().navigate(directions)\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"onClick: \", e)\n                    }\n                }\n\n                override fun onApprove(position: Int, pendingUser: PendingUser) {\n                    val resourceLiveData = viewModel.approveUsers(listOf(pendingUser.user))\n                    observeApprovalChange(resourceLiveData, position, pendingUser)\n                }\n\n                override fun onDeny(position: Int, pendingUser: PendingUser) {\n                    val resourceLiveData = viewModel.denyUsers(listOf(pendingUser.user))\n                    observeApprovalChange(resourceLiveData, position, pendingUser)\n                }\n            })\n            binding.pendingMembers.adapter = pendingUsersAdapter\n            binding.pendingMembersGroup.visibility = View.VISIBLE\n            isPendingRequestsSetupDone = true\n        }\n        pendingUsersAdapter?.submitPendingRequests(requests)\n    }\n\n    private fun observeDetailsChange(resourceLiveData: LiveData<Resource<Any?>>) {\n        resourceLiveData.observe(viewLifecycleOwner, { resource: Resource<Any?>? ->\n            if (resource == null) return@observe\n            when (resource.status) {\n                Resource.Status.SUCCESS,\n                Resource.Status.LOADING,\n                -> {\n                }\n                Resource.Status.ERROR -> {\n                    if (resource.message != null) {\n                        Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (resource.resId != 0) {\n                        Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show()\n                    }\n                }\n            }\n        })\n    }\n\n    private fun observeApprovalChange(\n        detailsChangeResourceLiveData: LiveData<Resource<Any?>>,\n        position: Int,\n        pendingUser: PendingUser,\n    ) {\n        detailsChangeResourceLiveData.observe(viewLifecycleOwner, { resource: Resource<Any?>? ->\n            if (resource == null) return@observe\n            when (resource.status) {\n                Resource.Status.SUCCESS -> {\n                }\n                Resource.Status.LOADING -> pendingUser.isInProgress = true\n                Resource.Status.ERROR -> {\n                    pendingUser.isInProgress = false\n                    if (resource.message != null) {\n                        Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    if (resource.resId != 0) {\n                        Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show()\n                    }\n                }\n            }\n            pendingUsersAdapter?.notifyItemChanged(position)\n        })\n    }\n\n    override fun onPositiveButtonClicked(requestCode: Int) {\n        if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) {\n            approvalRequiredUsers?.let {\n                val detailsChangeResourceLiveData = viewModel.addMembers(it)\n                observeDetailsChange(detailsChangeResourceLiveData)\n            }\n            return\n        }\n        if (requestCode == LEAVE_THREAD_REQUEST_CODE) {\n            val resourceLiveData = viewModel.leave()\n            resourceLiveData.observe(viewLifecycleOwner, { resource: Resource<Any?>? ->\n                if (resource == null) return@observe\n                when (resource.status) {\n                    Resource.Status.SUCCESS -> {\n                        val directions = DirectMessageSettingsFragmentDirections.actionToInbox()\n                        NavHostFragment.findNavController(this).navigate(directions)\n                    }\n                    Resource.Status.ERROR -> {\n                        binding.leave.isEnabled = true\n                        if (resource.message != null) {\n                            Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show()\n                        }\n                        if (resource.resId != 0) {\n                            Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show()\n                        }\n                    }\n                    Resource.Status.LOADING -> binding.leave.isEnabled = false\n                }\n            })\n            return\n        }\n        if (requestCode == END_THREAD_REQUEST_CODE) {\n            val resourceLiveData = viewModel.end()\n            resourceLiveData.observe(viewLifecycleOwner, { resource: Resource<Any?>? ->\n                if (resource == null) return@observe\n                when (resource.status) {\n                    Resource.Status.SUCCESS -> {\n                    }\n                    Resource.Status.ERROR -> {\n                        binding.end.isEnabled = true\n                        if (resource.message != null) {\n                            Snackbar.make(binding.root, resource.message, Snackbar.LENGTH_LONG).show()\n                        }\n                        if (resource.resId != 0) {\n                            Snackbar.make(binding.root, resource.resId, Snackbar.LENGTH_LONG).show()\n                        }\n                    }\n                    Resource.Status.LOADING -> binding.end.isEnabled = false\n                }\n            })\n        }\n    }\n\n    override fun onNegativeButtonClicked(requestCode: Int) {\n        if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) {\n            approvalRequiredUsers = null\n        }\n    }\n\n    override fun onNeutralButtonClicked(requestCode: Int) {}\n\n    companion object {\n        private const val APPROVAL_REQUIRED_REQUEST_CODE = 200\n        private const val LEAVE_THREAD_REQUEST_CODE = 201\n        private const val END_THREAD_REQUEST_CODE = 202\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java",
    "content": "package awais.instagrabber.fragments.directmessages;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.Toast;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.view.menu.ActionMenuItemView;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\nimport androidx.core.view.WindowInsetsAnimationControlListenerCompat;\nimport androidx.core.view.WindowInsetsAnimationControllerCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavBackStackEntry;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.ItemTouchHelper;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.recyclerview.widget.SimpleItemAnimator;\nimport androidx.transition.TransitionManager;\nimport androidx.vectordrawable.graphics.drawable.Animatable2Compat;\nimport androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;\n\nimport com.google.android.material.badge.BadgeDrawable;\nimport com.google.android.material.badge.BadgeUtils;\nimport com.google.android.material.internal.ToolbarUtils;\nimport com.google.android.material.snackbar.Snackbar;\nimport com.google.common.collect.ImmutableList;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.CameraActivity;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.DirectItemsAdapter;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemLongClickListener;\nimport awais.instagrabber.adapters.DirectItemsAdapter.DirectItemOrHeader;\nimport awais.instagrabber.adapters.DirectReactionsAdapter;\nimport awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;\nimport awais.instagrabber.animations.CubicBezierInterpolator;\nimport awais.instagrabber.customviews.InsetsAnimationLinearLayout;\nimport awais.instagrabber.customviews.KeyNotifyingEmojiEditText;\nimport awais.instagrabber.customviews.RecordView;\nimport awais.instagrabber.customviews.Tooltip;\nimport awais.instagrabber.customviews.emoji.Emoji;\nimport awais.instagrabber.customviews.emoji.EmojiBottomSheetDialog;\nimport awais.instagrabber.customviews.emoji.EmojiPicker;\nimport awais.instagrabber.customviews.helpers.ControlFocusInsetsAnimationCallback;\nimport awais.instagrabber.customviews.helpers.EmojiPickerInsetsAnimationCallback;\nimport awais.instagrabber.customviews.helpers.HeaderItemDecoration;\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;\nimport awais.instagrabber.customviews.helpers.SimpleImeAnimationController;\nimport awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback;\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter;\nimport awais.instagrabber.customviews.helpers.TranslateDeferringInsetsAnimationCallback;\nimport awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;\nimport awais.instagrabber.dialogs.DirectItemReactionDialogFragment;\nimport awais.instagrabber.dialogs.GifPickerBottomDialogFragment;\nimport awais.instagrabber.fragments.UserSearchMode;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.requests.StoryViewerOptions;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemLink;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.DMUtils;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.PermissionUtils;\nimport awais.instagrabber.utils.ResponseBodyUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.AppStateViewModel;\nimport awais.instagrabber.viewmodels.DirectThreadViewModel;\nimport awais.instagrabber.viewmodels.factories.DirectThreadViewModelFactory;\n\npublic class DirectMessageThreadFragment extends Fragment implements DirectReactionsAdapter.OnReactionClickListener,\n        EmojiPicker.OnEmojiClickListener {\n    private static final String TAG = DirectMessageThreadFragment.class.getSimpleName();\n    private static final int AUDIO_RECORD_PERM_REQUEST_CODE = 1000;\n    private static final int CAMERA_REQUEST_CODE = 200;\n    private static final int FILE_PICKER_REQUEST_CODE = 500;\n    private static final String TRANSLATION_Y = \"translationY\";\n\n    private DirectItemsAdapter itemsAdapter;\n    private MainActivity fragmentActivity;\n    private DirectThreadViewModel viewModel;\n    private InsetsAnimationLinearLayout root;\n    private boolean shouldRefresh = true;\n    private List<DirectItemOrHeader> itemOrHeaders;\n    private FragmentDirectMessagesThreadBinding binding;\n    private Tooltip tooltip;\n    private float initialSendX;\n    private ActionBar actionBar;\n    private AppStateViewModel appStateViewModel;\n    private Runnable prevTitleRunnable;\n    private AnimatorSet animatorSet;\n    private boolean isRecording;\n    private DirectItemReactionDialogFragment reactionDialogFragment;\n    private DirectItem itemToForward;\n    private MutableLiveData<Object> backStackSavedStateResultLiveData;\n    private int prevLength;\n    private BadgeDrawable pendingRequestCountBadgeDrawable;\n    private boolean isPendingRequestCountBadgeAttached = false;\n    private ItemTouchHelper itemTouchHelper;\n    private LiveData<Boolean> pendingLiveData;\n    private LiveData<DirectThread> threadLiveData;\n    private LiveData<Integer> inputModeLiveData;\n    private LiveData<String> threadTitleLiveData;\n    private LiveData<Resource<Object>> fetchingLiveData;\n    private LiveData<List<DirectItem>> itemsLiveData;\n    private LiveData<DirectItem> replyToItemLiveData;\n    private LiveData<Integer> pendingRequestsCountLiveData;\n    private LiveData<List<User>> usersLiveData;\n    private boolean autoMarkAsSeen = false;\n    private MenuItem markAsSeenMenuItem;\n    private DirectItem addReactionItem;\n    private TranslateDeferringInsetsAnimationCallback inputHolderAnimationCallback;\n    private TranslateDeferringInsetsAnimationCallback chatsAnimationCallback;\n    private EmojiPickerInsetsAnimationCallback emojiPickerAnimationCallback;\n    private boolean hasKbOpenedOnce;\n    private boolean wasToggled;\n    private SwipeAndRestoreItemTouchHelperCallback touchHelperCallback;\n\n    private final AppExecutors appExecutors = AppExecutors.INSTANCE;\n    private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {\n        @Override\n        public void onAnimationEnd(final Drawable drawable) {\n            AnimatedVectorDrawableCompat.unregisterAnimationCallback(drawable, this);\n            setSendToMicIcon();\n        }\n    };\n    private final Animatable2Compat.AnimationCallback sendToMicAnimationCallback = new Animatable2Compat.AnimationCallback() {\n        @Override\n        public void onAnimationEnd(final Drawable drawable) {\n            AnimatedVectorDrawableCompat.unregisterAnimationCallback(drawable, this);\n            setMicToSendIcon();\n        }\n    };\n    private final DirectItemCallback directItemCallback = new DirectItemCallback() {\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = DirectMessageThreadFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToUser(mention);\n        }\n\n        @Override\n        public void onLocationClick(final long locationId) {\n            try {\n                final NavDirections action = DirectMessageThreadFragmentDirections.actionToLocation(locationId);\n                NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            final Context context = getContext();\n            if (context == null) return;\n            Utils.openURL(context, url);\n        }\n\n        @Override\n        public void onEmailClick(final String email) {\n            final Context context = getContext();\n            if (context == null) return;\n            Utils.openEmailAddress(context, email);\n        }\n\n        @Override\n        public void onMediaClick(final Media media, final int index) {\n            if (media.isReelMedia()) {\n                try {\n                    final String pk = media.getPk();\n                    if (pk == null) return;\n                    final long mediaId = Long.parseLong(pk);\n                    final User user = media.getUser();\n                    if (user == null) return;\n                    final String username = user.getUsername();\n                    final NavDirections action = DirectMessageThreadFragmentDirections.actionToStory(StoryViewerOptions.forStory(mediaId, username));\n                    NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);\n                } catch (Exception e) {\n                    Log.e(TAG, \"onMediaClick (story): \", e);\n                }\n                return;\n            }\n            try {\n                final NavDirections actionToPost = DirectMessageThreadFragmentDirections.actionToPost(media, index);\n                NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(actionToPost);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n        }\n\n        @Override\n        public void onStoryClick(final DirectItemStoryShare storyShare) {\n            try {\n                final String pk = storyShare.getReelId();\n                if (pk == null) return;\n                final long mediaId = Long.parseLong(pk);\n                final Media media = storyShare.getMedia();\n                if (media == null) return;\n                final User user = media.getUser();\n                if (user == null) return;\n                final String username = user.getUsername();\n                final NavDirections action = DirectMessageThreadFragmentDirections.actionToStory(StoryViewerOptions.forUser(mediaId, username));\n                NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onStoryClick: \", e);\n            }\n        }\n\n        @Override\n        public void onReaction(final DirectItem item, final Emoji emoji) {\n            if (item == null || emoji == null) return;\n            final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(item, emoji);\n            resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));\n        }\n\n        @Override\n        public void onReactionClick(final DirectItem item, final int position) {\n            showReactionsDialog(item);\n        }\n\n        @Override\n        public void onOptionSelect(final DirectItem item, final int itemId, final Function<DirectItem, Void> cb) {\n            if (itemId == R.id.unsend) {\n                handleSentMessage(viewModel.unsend(item));\n                return;\n            }\n            if (itemId == R.id.forward) {\n                itemToForward = item;\n                final NavDirections actionGlobalUserSearch = DirectMessageThreadFragmentDirections\n                        .actionToUserSearch()\n                        .setTitle(getString(R.string.forward))\n                        .setActionLabel(getString(R.string.send))\n                        .setShowGroups(true)\n                        .setMultiple(true)\n                        .setSearchMode(UserSearchMode.RAVEN);\n                NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(actionGlobalUserSearch);\n            }\n            if (itemId == R.id.download) {\n                downloadItem(item);\n                return;\n            }\n            // otherwise call callback if present\n            if (cb != null) {\n                cb.apply(item);\n            }\n        }\n\n        @Override\n        public void onAddReactionListener(final DirectItem item) {\n            if (item == null) return;\n            addReactionItem = item;\n            final EmojiBottomSheetDialog emojiBottomSheetDialog = EmojiBottomSheetDialog.newInstance();\n            emojiBottomSheetDialog.show(getChildFragmentManager(), EmojiBottomSheetDialog.TAG);\n        }\n    };\n    private final DirectItemLongClickListener directItemLongClickListener = position -> {\n        // viewModel.setSelectedPosition(position);\n    };\n    private final Observer<Object> backStackSavedStateObserver = result -> {\n        if (result == null) return;\n        if (result instanceof Uri) {\n            final Uri uri = (Uri) result;\n            handleSentMessage(viewModel.sendUri(uri));\n        } else if ((result instanceof RankedRecipient)) {\n            // Log.d(TAG, \"result: \" + result);\n            if (itemToForward != null) {\n                viewModel.forward((RankedRecipient) result, itemToForward);\n            }\n        } else if ((result instanceof Set)) {\n            try {\n                // Log.d(TAG, \"result: \" + result);\n                if (itemToForward != null) {\n                    //noinspection unchecked\n                    viewModel.forward((Set<RankedRecipient>) result, itemToForward);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"forward result: \", e);\n            }\n        }\n        // clear result\n        backStackSavedStateResultLiveData.postValue(null);\n    };\n    private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);\n    private final MutableLiveData<Boolean> emojiPickerVisible = new MutableLiveData<>(false);\n    private final MutableLiveData<Boolean> kbVisible = new MutableLiveData<>(false);\n    private final OnBackPressedCallback onEmojiPickerBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            emojiPickerVisible.postValue(false);\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);\n        autoMarkAsSeen = Utils.settingsHelper.getBoolean(PreferenceKeys.DM_MARK_AS_SEEN);\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments);\n        final Resource<User> currentUserResource = appStateViewModel.getCurrentUser();\n        if (currentUserResource == null) return;\n        final User currentUser = currentUserResource.data;\n        if (currentUser == null) return;\n        final DirectThreadViewModelFactory viewModelFactory = new DirectThreadViewModelFactory(\n                fragmentActivity.getApplication(),\n                fragmentArgs.getThreadId(),\n                fragmentArgs.getPending(),\n                currentUser\n        );\n        viewModel = new ViewModelProvider(this, viewModelFactory).get(DirectThreadViewModel.class);\n        setHasOptionsMenu(true);\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentDirectMessagesThreadBinding.inflate(inflater, container, false);\n        binding.send.setRecordView(binding.recordView);\n        root = binding.getRoot();\n        final Context context = getContext();\n        if (context == null) {\n            return root;\n        }\n        tooltip = new Tooltip(context, root, getResources().getColor(R.color.grey_400), getResources().getColor(R.color.black));\n        // todo check has camera and remove view\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        // WindowCompat.setDecorFitsSystemWindows(fragmentActivity.getWindow(), false);\n        if (!shouldRefresh) return;\n        init();\n        binding.send.post(() -> initialSendX = binding.send.getX());\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.dm_thread_menu, menu);\n        markAsSeenMenuItem = menu.findItem(R.id.mark_as_seen);\n        if (markAsSeenMenuItem != null) {\n            if (autoMarkAsSeen) {\n                markAsSeenMenuItem.setVisible(false);\n            } else {\n                markAsSeenMenuItem.setEnabled(false);\n            }\n        }\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        final int itemId = item.getItemId();\n        if (itemId == R.id.info) {\n            final Boolean pending = viewModel.isPending().getValue();\n            final NavDirections directions = DirectMessageThreadFragmentDirections\n                    .actionToSettings(viewModel.getThreadId(), null)\n                    .setPending(pending != null && pending);\n            NavHostFragment.findNavController(this).navigate(directions);\n            return true;\n        }\n        if (itemId == R.id.mark_as_seen) {\n            handleMarkAsSeen(item);\n            return true;\n        }\n        if (itemId == R.id.refresh && viewModel != null) {\n            viewModel.refreshChats();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    private void handleMarkAsSeen(@NonNull final MenuItem item) {\n        final LiveData<Resource<Object>> resourceLiveData = viewModel.markAsSeen();\n        resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {\n            @Override\n            public void onChanged(final Resource<Object> resource) {\n                try {\n                    if (resource == null) return;\n                    final Context context = getContext();\n                    if (context == null) return;\n                    switch (resource.status) {\n                        case SUCCESS:\n                            Toast.makeText(context, R.string.marked_as_seen, Toast.LENGTH_SHORT).show();\n                        case LOADING:\n                            item.setEnabled(false);\n                            break;\n                        case ERROR:\n                            item.setEnabled(true);\n                            if (resource.message != null) {\n                                Snackbar.make(context, binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();\n                                return;\n                            }\n                            if (resource.resId != 0) {\n                                Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show();\n                                return;\n                            }\n                            break;\n                    }\n                } finally {\n                    resourceLiveData.removeObserver(this);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (requestCode == FILE_PICKER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {\n            if (data == null || data.getData() == null) {\n                Log.w(TAG, \"data is null!\");\n                return;\n            }\n            final Context context = getContext();\n            if (context == null) {\n                Log.w(TAG, \"conetxt is null!\");\n                return;\n            }\n            final Uri uri = data.getData();\n            final String mimeType = Utils.getMimeType(uri, context.getContentResolver());\n            if (mimeType != null && mimeType.startsWith(\"image\")) {\n                navigateToImageEditFragment(uri);\n                return;\n            }\n            handleSentMessage(viewModel.sendUri(uri));\n        }\n        if (requestCode == CAMERA_REQUEST_CODE && resultCode == Activity.RESULT_OK) {\n            if (data == null || data.getData() == null) {\n                Log.w(TAG, \"data is null!\");\n                return;\n            }\n            final Uri uri = data.getData();\n            navigateToImageEditFragment(uri);\n        }\n    }\n\n    @Override\n    public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        final Context context = getContext();\n        if (context == null) return;\n        if (requestCode == AUDIO_RECORD_PERM_REQUEST_CODE) {\n            if (PermissionUtils.hasAudioRecordPerms(context)) {\n                Toast.makeText(context, \"You can send voice messages now!\", Toast.LENGTH_LONG).show();\n                return;\n            }\n            Toast.makeText(context, \"Require RECORD_AUDIO permission\", Toast.LENGTH_LONG).show();\n        }\n    }\n\n    @Override\n    public void onPause() {\n        if (isRecording) {\n            binding.recordView.cancelRecording(binding.send);\n        }\n        emojiPickerVisible.postValue(false);\n        kbVisible.postValue(false);\n        binding.inputHolder.setTranslationY(0);\n        binding.chats.setTranslationY(0);\n        binding.emojiPicker.setTranslationY(0);\n        removeObservers();\n        super.onPause();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (initialSendX != 0) {\n            binding.send.setX(initialSendX);\n        }\n        binding.send.stopScale();\n        final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n        onBackPressedDispatcher.addCallback(onEmojiPickerBackPressedCallback);\n        setupBackStackResultObserver();\n        setObservers();\n        // attachPendingRequestsBadge(viewModel.getPendingRequestsCount().getValue());\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        cleanup();\n    }\n\n    @Override\n    public void onDestroy() {\n        viewModel.deleteThreadIfRequired();\n        super.onDestroy();\n    }\n\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private void cleanup() {\n        if (prevTitleRunnable != null) {\n            appExecutors.getMainThread().cancel(prevTitleRunnable);\n        }\n        for (int childCount = binding.chats.getChildCount(), i = 0; i < childCount; ++i) {\n            final RecyclerView.ViewHolder holder = binding.chats.getChildViewHolder(binding.chats.getChildAt(i));\n            if (holder == null) continue;\n            if (holder instanceof DirectItemViewHolder) {\n                ((DirectItemViewHolder) holder).cleanup();\n            }\n        }\n        isPendingRequestCountBadgeAttached = false;\n        if (pendingRequestCountBadgeDrawable != null) {\n            @SuppressLint(\"RestrictedApi\") final ActionMenuItemView menuItemView = ToolbarUtils\n                    .getActionMenuItemView(fragmentActivity.getToolbar(), R.id.info);\n            if (menuItemView != null) {\n                BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);\n            }\n            pendingRequestCountBadgeDrawable = null;\n        }\n    }\n\n    private void init() {\n        final Context context = getContext();\n        if (context == null) return;\n        if (getArguments() == null) return;\n        actionBar = fragmentActivity.getSupportActionBar();\n        setupList();\n    }\n\n    private void setupList() {\n        final Context context = getContext();\n        if (context == null) return;\n        binding.chats.setItemViewCacheSize(20);\n        final LinearLayoutManager layoutManager = new LinearLayoutManager(context);\n        layoutManager.setReverseLayout(true);\n        // layoutManager.setStackFromEnd(false);\n        // binding.messageList.addItemDecoration(new VerticalSpaceItemDecoration(3));\n        final RecyclerView.ItemAnimator animator = binding.chats.getItemAnimator();\n        if (animator instanceof SimpleItemAnimator) {\n            final SimpleItemAnimator itemAnimator = (SimpleItemAnimator) animator;\n            itemAnimator.setSupportsChangeAnimations(false);\n        }\n        binding.chats.setLayoutManager(layoutManager);\n        binding.chats.addOnScrollListener(new RecyclerLazyLoaderAtEdge(layoutManager, true, page -> viewModel.fetchChats()));\n        final HeaderItemDecoration headerItemDecoration = new HeaderItemDecoration(binding.chats, itemPosition -> {\n            if (itemOrHeaders == null || itemOrHeaders.isEmpty()) return false;\n            try {\n                final DirectItemOrHeader itemOrHeader = itemOrHeaders.get(itemPosition);\n                return itemOrHeader.isHeader();\n            } catch (IndexOutOfBoundsException e) {\n                return false;\n            }\n        });\n        binding.chats.addItemDecoration(headerItemDecoration);\n    }\n\n    private void setObservers() {\n        if (viewModel == null) return;\n        threadLiveData = viewModel.getThread();\n        // if (threadLiveData == null) {\n        //     final NavController navController = NavHostFragment.findNavController(this);\n        //     navController.navigateUp();\n        //     return;\n        // }\n        pendingLiveData = viewModel.isPending();\n        pendingLiveData.observe(getViewLifecycleOwner(), isPending -> {\n            if (isPending == null) {\n                hideInput();\n                return;\n            }\n            if (isPending) {\n                showPendingOptions();\n                return;\n            }\n            hidePendingOptions();\n            final Integer inputMode = viewModel.getInputMode().getValue();\n            if (inputMode != null && inputMode == 1) return;\n            showInput();\n        });\n        inputModeLiveData = viewModel.getInputMode();\n        inputModeLiveData.observe(getViewLifecycleOwner(), inputMode -> {\n            final Boolean isPending = viewModel.isPending().getValue();\n            if (isPending != null && isPending || inputMode == null) return;\n            setupInput(inputMode);\n            if (inputMode == 0) {\n                setupTouchHelper();\n                return;\n            }\n            if (inputMode == 1) {\n                hideInput();\n            }\n        });\n        threadTitleLiveData = viewModel.getThreadTitle();\n        threadTitleLiveData.observe(getViewLifecycleOwner(), this::setTitle);\n        fetchingLiveData = viewModel.isFetching();\n        fetchingLiveData.observe(getViewLifecycleOwner(), fetchingResource -> {\n            if (fetchingResource == null) return;\n            switch (fetchingResource.status) {\n                case SUCCESS:\n                case ERROR:\n                    setTitle(viewModel.getThreadTitle().getValue());\n                    if (fetchingResource.message != null) {\n                        Snackbar.make(binding.getRoot(), fetchingResource.message, Snackbar.LENGTH_LONG).show();\n                    }\n                    if (fetchingResource.resId != 0) {\n                        Snackbar.make(binding.getRoot(), fetchingResource.resId, Snackbar.LENGTH_LONG).show();\n                    }\n                    break;\n                case LOADING:\n                    setTitle(getString(R.string.dms_thread_updating));\n                    break;\n            }\n        });\n        // final ItemsAdapterDataMerger itemsAdapterDataMerger = new ItemsAdapterDataMerger(appStateViewModel.getCurrentUser(), viewModel.getThread());\n        // itemsAdapterDataMerger.observe(getViewLifecycleOwner(), userThreadPair -> {\n        //     viewModel.setCurrentUser(userThreadPair.first);\n        //     setupItemsAdapter(userThreadPair.first, userThreadPair.second);\n        // });\n        threadLiveData.observe(getViewLifecycleOwner(), this::setupItemsAdapter);\n        itemsLiveData = viewModel.getItems();\n        itemsLiveData.observe(getViewLifecycleOwner(), this::submitItemsToAdapter);\n        replyToItemLiveData = viewModel.getReplyToItem();\n        replyToItemLiveData.observe(getViewLifecycleOwner(), item -> {\n            if (item == null) {\n                if (binding.input.length() == 0) {\n                    showExtraInputOption(true);\n                }\n                binding.getRoot().post(() -> {\n                    TransitionManager.beginDelayedTransition(binding.getRoot());\n                    binding.replyBg.setVisibility(View.GONE);\n                    binding.replyInfo.setVisibility(View.GONE);\n                    binding.replyPreviewImage.setVisibility(View.GONE);\n                    binding.replyCancel.setVisibility(View.GONE);\n                    binding.replyPreviewText.setVisibility(View.GONE);\n                });\n                return;\n            }\n            showExtraInputOption(false);\n            binding.getRoot().postDelayed(() -> {\n                binding.replyBg.setVisibility(View.VISIBLE);\n                binding.replyInfo.setVisibility(View.VISIBLE);\n                binding.replyPreviewImage.setVisibility(View.VISIBLE);\n                binding.replyCancel.setVisibility(View.VISIBLE);\n                binding.replyPreviewText.setVisibility(View.VISIBLE);\n                if (item.getUserId() == viewModel.getViewerId()) {\n                    binding.replyInfo.setText(R.string.replying_to_yourself);\n                } else {\n                    final User user = viewModel.getUser(item.getUserId());\n                    if (user != null) {\n                        binding.replyInfo.setText(getString(R.string.replying_to_user, user.getFullName()));\n                    } else {\n                        binding.replyInfo.setVisibility(View.GONE);\n                    }\n                }\n                final String previewText = getDirectItemPreviewText(item);\n                binding.replyPreviewText.setText(TextUtils.isEmpty(previewText) ? getString(R.string.message) : previewText);\n                final String previewImageUrl = getDirectItemPreviewImageUrl(item);\n                if (TextUtils.isEmpty(previewImageUrl)) {\n                    binding.replyPreviewImage.setVisibility(View.GONE);\n                } else {\n                    binding.replyPreviewImage.setImageURI(previewImageUrl);\n                }\n                binding.replyCancel.setOnClickListener(v -> viewModel.setReplyToItem(null));\n            }, 200);\n        });\n        inputLength.observe(getViewLifecycleOwner(), length -> {\n            if (length == null) return;\n            final boolean hasReplyToItem = viewModel.getReplyToItem().getValue() != null;\n            if (hasReplyToItem) {\n                prevLength = length;\n                return;\n            }\n            if ((prevLength == 0 && length != 0) || (prevLength != 0 && length == 0)) {\n                showExtraInputOption(length == 0);\n            }\n            prevLength = length;\n        });\n        pendingRequestsCountLiveData = viewModel.getPendingRequestsCount();\n        pendingRequestsCountLiveData.observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);\n        usersLiveData = viewModel.getUsers();\n        usersLiveData.observe(getViewLifecycleOwner(), users -> {\n            if (users == null || users.isEmpty()) return;\n            final User user = users.get(0);\n            binding.acceptPendingRequestQuestion.setText(getString(R.string.accept_request_from_user, user.getUsername(), user.getFullName()));\n        });\n    }\n\n    private void setupTouchHelper() {\n        final Context context = getContext();\n        if (context == null) return;\n        touchHelperCallback = new SwipeAndRestoreItemTouchHelperCallback(\n                context,\n                (adapterPosition, viewHolder) -> {\n                    if (itemsAdapter == null) return;\n                    final DirectItemOrHeader directItemOrHeader = itemsAdapter.getList().get(adapterPosition);\n                    if (directItemOrHeader.isHeader()) return;\n                    viewModel.setReplyToItem(directItemOrHeader.item);\n                }\n        );\n        itemTouchHelper = new ItemTouchHelper(touchHelperCallback);\n        itemTouchHelper.attachToRecyclerView(binding.chats);\n    }\n\n    private void removeObservers() {\n        pendingLiveData.removeObservers(getViewLifecycleOwner());\n        inputModeLiveData.removeObservers(getViewLifecycleOwner());\n        threadTitleLiveData.removeObservers(getViewLifecycleOwner());\n        fetchingLiveData.removeObservers(getViewLifecycleOwner());\n        threadLiveData.removeObservers(getViewLifecycleOwner());\n        itemsLiveData.removeObservers(getViewLifecycleOwner());\n        replyToItemLiveData.removeObservers(getViewLifecycleOwner());\n        inputLength.removeObservers(getViewLifecycleOwner());\n        pendingRequestsCountLiveData.removeObservers(getViewLifecycleOwner());\n        usersLiveData.removeObservers(getViewLifecycleOwner());\n\n    }\n\n    private void hidePendingOptions() {\n        binding.acceptPendingRequestQuestion.setVisibility(View.GONE);\n        binding.decline.setVisibility(View.GONE);\n        binding.accept.setVisibility(View.GONE);\n    }\n\n    private void showPendingOptions() {\n        binding.acceptPendingRequestQuestion.setVisibility(View.VISIBLE);\n        binding.decline.setVisibility(View.VISIBLE);\n        binding.accept.setVisibility(View.VISIBLE);\n        binding.accept.setOnClickListener(v -> {\n            final LiveData<Resource<Object>> resourceLiveData = viewModel.acceptRequest();\n            handlePendingChangeResource(resourceLiveData, false);\n        });\n        binding.decline.setOnClickListener(v -> {\n            final LiveData<Resource<Object>> resourceLiveData = viewModel.declineRequest();\n            handlePendingChangeResource(resourceLiveData, true);\n        });\n    }\n\n    private void handlePendingChangeResource(final LiveData<Resource<Object>> resourceLiveData, final boolean isDecline) {\n        resourceLiveData.observe(getViewLifecycleOwner(), resource -> {\n            if (resource == null) return;\n            final Resource.Status status = resource.status;\n            switch (status) {\n                case SUCCESS:\n                    resourceLiveData.removeObservers(getViewLifecycleOwner());\n                    if (isDecline) {\n                        removeObservers();\n                        viewModel.removeThread();\n                        final NavController navController = NavHostFragment.findNavController(this);\n                        navController.navigateUp();\n                        return;\n                    }\n                    removeObservers();\n                    viewModel.moveFromPending();\n                    setObservers();\n                    break;\n                case LOADING:\n                    break;\n                case ERROR:\n                    if (resource.message != null) {\n                        Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();\n                    }\n                    if (resource.resId != 0) {\n                        Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show();\n                    }\n                    resourceLiveData.removeObservers(getViewLifecycleOwner());\n                    break;\n            }\n        });\n    }\n\n    private void hideInput() {\n        binding.emojiToggle.setVisibility(View.GONE);\n        binding.gif.setVisibility(View.GONE);\n        binding.camera.setVisibility(View.GONE);\n        binding.gallery.setVisibility(View.GONE);\n        binding.input.setVisibility(View.GONE);\n        binding.inputBg.setVisibility(View.GONE);\n        binding.recordView.setVisibility(View.GONE);\n        binding.send.setVisibility(View.GONE);\n        if (itemTouchHelper != null) {\n            itemTouchHelper.attachToRecyclerView(null);\n        }\n    }\n\n    private void showInput() {\n        binding.emojiToggle.setVisibility(View.VISIBLE);\n        binding.gif.setVisibility(View.VISIBLE);\n        binding.camera.setVisibility(View.VISIBLE);\n        binding.gallery.setVisibility(View.VISIBLE);\n        binding.input.setVisibility(View.VISIBLE);\n        binding.inputBg.setVisibility(View.VISIBLE);\n        binding.recordView.setVisibility(View.VISIBLE);\n        binding.send.setVisibility(View.VISIBLE);\n        if (itemTouchHelper != null) {\n            itemTouchHelper.attachToRecyclerView(binding.chats);\n        }\n    }\n\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private void attachPendingRequestsBadge(@Nullable final Integer count) {\n        if (pendingRequestCountBadgeDrawable == null) {\n            final Context context = getContext();\n            if (context == null) return;\n            pendingRequestCountBadgeDrawable = BadgeDrawable.create(context);\n        }\n        if (count == null || count == 0) {\n            @SuppressLint(\"RestrictedApi\") final ActionMenuItemView menuItemView = ToolbarUtils\n                    .getActionMenuItemView(fragmentActivity.getToolbar(), R.id.info);\n            if (menuItemView != null) {\n                BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);\n            }\n            isPendingRequestCountBadgeAttached = false;\n            pendingRequestCountBadgeDrawable.setNumber(0);\n            return;\n        }\n        if (pendingRequestCountBadgeDrawable.getNumber() == count) return;\n        pendingRequestCountBadgeDrawable.setNumber(count);\n        if (!isPendingRequestCountBadgeAttached) {\n            BadgeUtils.attachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);\n            isPendingRequestCountBadgeAttached = true;\n        }\n    }\n\n    private void showExtraInputOption(final boolean show) {\n        if (show) {\n            if (!binding.send.isListenForRecord()) {\n                binding.send.setListenForRecord(true);\n                startIconAnimation();\n            }\n            binding.gif.setVisibility(View.VISIBLE);\n            binding.camera.setVisibility(View.VISIBLE);\n            binding.gallery.setVisibility(View.VISIBLE);\n            return;\n        }\n        if (binding.send.isListenForRecord()) {\n            binding.send.setListenForRecord(false);\n            startIconAnimation();\n        }\n        binding.gif.setVisibility(View.GONE);\n        binding.camera.setVisibility(View.GONE);\n        binding.gallery.setVisibility(View.GONE);\n    }\n\n    private String getDirectItemPreviewText(@NonNull final DirectItem item) {\n        final DirectItemType itemType = item.getItemType();\n        if (itemType == null) return \"\";\n        switch (itemType) {\n            case TEXT:\n                return item.getText();\n            case LINK:\n                final DirectItemLink link = item.getLink();\n                if (link == null) return \"\";\n                return link.getText();\n            case MEDIA: {\n                final Media media = item.getMedia();\n                if (media == null) return \"\";\n                return getMediaPreviewTextString(media);\n            }\n            case RAVEN_MEDIA: {\n                final DirectItemVisualMedia visualMedia = item.getVisualMedia();\n                if (visualMedia == null) return \"\";\n                final Media media = visualMedia.getMedia();\n                if (media == null) return \"\";\n                return getMediaPreviewTextString(media);\n            }\n            case VOICE_MEDIA:\n                return getString(R.string.voice_message);\n            case MEDIA_SHARE:\n                return getString(R.string.post);\n            case REEL_SHARE:\n                final DirectItemReelShare reelShare = item.getReelShare();\n                if (reelShare == null) return \"\";\n                return reelShare.getText();\n        }\n        return \"\";\n    }\n\n    @NonNull\n    private String getMediaPreviewTextString(@NonNull final Media media) {\n        final MediaItemType mediaType = media.getType();\n        if (mediaType == null) return \"\";\n        switch (mediaType) {\n            case MEDIA_TYPE_IMAGE:\n                return getString(R.string.photo);\n            case MEDIA_TYPE_VIDEO:\n                return getString(R.string.video);\n            default:\n                return \"\";\n        }\n    }\n\n    @Nullable\n    private String getDirectItemPreviewImageUrl(@NonNull final DirectItem item) {\n        final DirectItemType itemType = item.getItemType();\n        if (itemType == null) return null;\n        switch (itemType) {\n            case TEXT:\n            case LINK:\n            case VOICE_MEDIA:\n            case REEL_SHARE:\n                return null;\n            case MEDIA: {\n                final Media media = item.getMedia();\n                return ResponseBodyUtils.getThumbUrl(media);\n            }\n            case RAVEN_MEDIA: {\n                final DirectItemVisualMedia visualMedia = item.getVisualMedia();\n                if (visualMedia == null) return null;\n                final Media media = visualMedia.getMedia();\n                return ResponseBodyUtils.getThumbUrl(media);\n            }\n            case MEDIA_SHARE: {\n                final Media media = item.getMediaShare();\n                return ResponseBodyUtils.getThumbUrl(media);\n            }\n        }\n        return null;\n    }\n\n    private void setupBackStackResultObserver() {\n        final NavController navController = NavHostFragment.findNavController(this);\n        final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();\n        if (backStackEntry != null) {\n            backStackSavedStateResultLiveData = backStackEntry.getSavedStateHandle().getLiveData(\"result\");\n            backStackSavedStateResultLiveData.observe(getViewLifecycleOwner(), backStackSavedStateObserver);\n        }\n    }\n\n    private void submitItemsToAdapter(final List<DirectItem> items) {\n        binding.chats.post(() -> {\n            if (autoMarkAsSeen) {\n                viewModel.markAsSeen();\n                return;\n            }\n            final DirectThread thread = threadLiveData.getValue();\n            if (thread == null) return;\n            if (markAsSeenMenuItem != null) {\n                markAsSeenMenuItem.setEnabled(!DMUtils.isRead(thread));\n            }\n        });\n        if (itemsAdapter == null) return;\n        itemsAdapter.submitList(items, () -> {\n            itemOrHeaders = itemsAdapter.getList();\n            binding.chats.post(() -> {\n                final RecyclerView.LayoutManager layoutManager = binding.chats.getLayoutManager();\n                if (layoutManager instanceof LinearLayoutManager) {\n                    final int position = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();\n                    if (position < 0) return;\n                    if (position == itemsAdapter.getItemCount() - 1) {\n                        viewModel.fetchChats();\n                    }\n                }\n            });\n        });\n    }\n\n    private void setupItemsAdapter(final DirectThread thread) {\n        if (thread == null) return;\n        if (itemsAdapter != null) {\n            if (itemsAdapter.getThread() == thread) return;\n            itemsAdapter.setThread(thread);\n            return;\n        }\n        final Resource<User> currentUserResource = appStateViewModel.getCurrentUser();\n        if (currentUserResource == null) return;\n        final User currentUser = currentUserResource.data;\n        if (currentUser == null) return;\n        itemsAdapter = new DirectItemsAdapter(currentUser, thread, directItemCallback, directItemLongClickListener);\n        itemsAdapter.setHasStableIds(true);\n        itemsAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);\n        binding.chats.setAdapter(itemsAdapter);\n        registerDataObserver();\n        final List<DirectItem> items = viewModel.getItems().getValue();\n        if (items != null && itemsAdapter.getItems() != items) {\n            submitItemsToAdapter(items);\n        }\n    }\n\n    private void registerDataObserver() {\n        itemsAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n\n            @Override\n            public void onItemRangeInserted(final int positionStart, final int itemCount) {\n                super.onItemRangeInserted(positionStart, itemCount);\n                final LinearLayoutManager layoutManager = (LinearLayoutManager) binding.chats.getLayoutManager();\n                if (layoutManager == null) return;\n                int firstVisiblePosition = layoutManager.findFirstCompletelyVisibleItemPosition();\n                if ((firstVisiblePosition == -1 || firstVisiblePosition == 0) && (positionStart == 0)) {\n                    binding.chats.scrollToPosition(0);\n                }\n            }\n        });\n    }\n\n    private void setupInput(@Nullable final Integer inputMode) {\n        if (inputMode != null && inputMode == 1) return;\n        final Context context = getContext();\n        if (context == null) return;\n        tooltip.setText(R.string.dms_thread_audio_hint);\n        setMicToSendIcon();\n        binding.recordView.setMinMillis(1000);\n        binding.recordView.setOnRecordListener(new RecordView.OnRecordListener() {\n            @Override\n            public void onStart() {\n                isRecording = true;\n                binding.input.setHint(null);\n                binding.gif.setVisibility(View.GONE);\n                binding.camera.setVisibility(View.GONE);\n                binding.gallery.setVisibility(View.GONE);\n                if (PermissionUtils.hasAudioRecordPerms(context)) {\n                    viewModel.startRecording();\n                    return;\n                }\n                PermissionUtils.requestAudioRecordPerms(DirectMessageThreadFragment.this, AUDIO_RECORD_PERM_REQUEST_CODE);\n            }\n\n            @Override\n            public void onCancel() {\n                Log.d(TAG, \"onCancel\");\n                // binding.input.setHint(\"Message\");\n                viewModel.stopRecording(true);\n                isRecording = false;\n            }\n\n            @Override\n            public void onFinish(final long recordTime) {\n                Log.d(TAG, \"onFinish\");\n                binding.input.setHint(\"Message\");\n                binding.gif.setVisibility(View.VISIBLE);\n                binding.camera.setVisibility(View.VISIBLE);\n                binding.gallery.setVisibility(View.VISIBLE);\n                viewModel.stopRecording(false);\n                isRecording = false;\n            }\n\n            @Override\n            public void onLessThanMin() {\n                Log.d(TAG, \"onLessThanMin\");\n                binding.input.setHint(\"Message\");\n                if (PermissionUtils.hasAudioRecordPerms(context)) {\n                    tooltip.show(binding.send);\n                }\n                binding.gif.setVisibility(View.VISIBLE);\n                binding.camera.setVisibility(View.VISIBLE);\n                binding.gallery.setVisibility(View.VISIBLE);\n                viewModel.stopRecording(true);\n                isRecording = false;\n            }\n        });\n        binding.recordView.setOnBasketAnimationEndListener(() -> {\n            binding.input.setHint(R.string.dms_thread_message_hint);\n            binding.gif.setVisibility(View.VISIBLE);\n            binding.camera.setVisibility(View.VISIBLE);\n            binding.gallery.setVisibility(View.VISIBLE);\n        });\n        binding.input.addTextChangedListener(new TextWatcherAdapter() {\n            // int prevLength = 0;\n\n            @Override\n            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {\n                final int length = s.length();\n                inputLength.postValue(length);\n            }\n        });\n        binding.send.setOnRecordClickListener(v -> {\n            final Editable text = binding.input.getText();\n            if (TextUtils.isEmpty(text)) return;\n            final LiveData<Resource<Object>> resourceLiveData = viewModel.sendText(text.toString());\n            resourceLiveData.observe(getViewLifecycleOwner(), resource -> handleSentMessage(resourceLiveData));\n            binding.input.setText(\"\");\n            viewModel.setReplyToItem(null);\n        });\n        binding.send.setOnRecordLongClickListener(v -> {\n            Log.d(TAG, \"setOnRecordLongClickListener\");\n            return true;\n        });\n        binding.input.setOnFocusChangeListener((v, hasFocus) -> {\n            if (!hasFocus) return;\n            final Boolean emojiPickerVisibleValue = emojiPickerVisible.getValue();\n            if (emojiPickerVisibleValue == null || !emojiPickerVisibleValue) return;\n            inputHolderAnimationCallback.setShouldTranslate(false);\n            chatsAnimationCallback.setShouldTranslate(false);\n            emojiPickerAnimationCallback.setShouldTranslate(false);\n        });\n        setupInsetsCallback();\n        setupEmojiPicker();\n        binding.gallery.setOnClickListener(v -> {\n            final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);\n            intent.setType(\"*/*\");\n            intent.addCategory(Intent.CATEGORY_OPENABLE);\n            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n            intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{\n                    \"image/*\",\n                    \"video/mp4\"\n            });\n            startActivityForResult(intent, FILE_PICKER_REQUEST_CODE);\n        });\n        binding.gif.setOnClickListener(v -> {\n            final GifPickerBottomDialogFragment gifPicker = GifPickerBottomDialogFragment.newInstance();\n            gifPicker.setOnSelectListener(giphyGif -> {\n                gifPicker.dismiss();\n                if (giphyGif == null) return;\n                handleSentMessage(viewModel.sendAnimatedMedia(giphyGif));\n            });\n            gifPicker.show(getChildFragmentManager(), \"GifPicker\");\n        });\n        binding.camera.setOnClickListener(v -> {\n            final Intent intent = new Intent(context, CameraActivity.class);\n            startActivityForResult(intent, CAMERA_REQUEST_CODE);\n        });\n    }\n\n    private void setupInsetsCallback() {\n        inputHolderAnimationCallback = new TranslateDeferringInsetsAnimationCallback(\n                binding.inputHolder,\n                WindowInsetsCompat.Type.systemBars(),\n                WindowInsetsCompat.Type.ime(),\n                WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE\n        );\n        ViewCompat.setWindowInsetsAnimationCallback(binding.inputHolder, inputHolderAnimationCallback);\n        chatsAnimationCallback = new TranslateDeferringInsetsAnimationCallback(\n                binding.chats,\n                WindowInsetsCompat.Type.systemBars(),\n                WindowInsetsCompat.Type.ime()\n        );\n        ViewCompat.setWindowInsetsAnimationCallback(binding.chats, chatsAnimationCallback);\n        emojiPickerAnimationCallback = new EmojiPickerInsetsAnimationCallback(\n                binding.emojiPicker,\n                WindowInsetsCompat.Type.systemBars(),\n                WindowInsetsCompat.Type.ime()\n        );\n        emojiPickerAnimationCallback.setKbVisibilityListener(this::onKbVisibilityChange);\n        ViewCompat.setWindowInsetsAnimationCallback(binding.emojiPicker, emojiPickerAnimationCallback);\n        ViewCompat.setWindowInsetsAnimationCallback(\n                binding.input,\n                new ControlFocusInsetsAnimationCallback(binding.input)\n        );\n        final SimpleImeAnimationController imeAnimController = root.getImeAnimController();\n        if (imeAnimController != null) {\n            imeAnimController.setAnimationControlListener(new WindowInsetsAnimationControlListenerCompat() {\n                @Override\n                public void onReady(@NonNull final WindowInsetsAnimationControllerCompat controller, final int types) {}\n\n                @Override\n                public void onFinished(@NonNull final WindowInsetsAnimationControllerCompat controller) {\n                    checkKbVisibility();\n                }\n\n                @Override\n                public void onCancelled(@Nullable final WindowInsetsAnimationControllerCompat controller) {\n                    checkKbVisibility();\n                }\n\n                private void checkKbVisibility() {\n                    final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(binding.getRoot());\n                    final boolean visible = rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime());\n                    onKbVisibilityChange(visible);\n                }\n            });\n        }\n    }\n\n    private void onKbVisibilityChange(final boolean kbVisible) {\n        this.kbVisible.postValue(kbVisible);\n        if (wasToggled) {\n            emojiPickerVisible.postValue(!kbVisible);\n            wasToggled = false;\n            return;\n        }\n        final Boolean emojiPickerVisibleValue = emojiPickerVisible.getValue();\n        if (kbVisible && emojiPickerVisibleValue != null && emojiPickerVisibleValue) {\n            emojiPickerVisible.postValue(false);\n            return;\n        }\n        if (!kbVisible) {\n            emojiPickerVisible.postValue(false);\n        }\n    }\n\n    private void startIconAnimation() {\n        final Drawable icon = binding.send.getIcon();\n        if (icon instanceof Animatable) {\n            final Animatable animatable = (Animatable) icon;\n            if (animatable.isRunning()) {\n                animatable.stop();\n            }\n            animatable.start();\n        }\n    }\n\n    private void navigateToImageEditFragment(final String path) {\n        navigateToImageEditFragment(Uri.fromFile(new File(path)));\n    }\n\n    private void navigateToImageEditFragment(final Uri uri) {\n        try {\n            final NavDirections navDirections = DirectMessageThreadFragmentDirections.actionToImageEdit(uri);\n            NavHostFragment.findNavController(this).navigate(navDirections);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToImageEditFragment: \", e);\n        }\n    }\n\n    private void handleSentMessage(final LiveData<Resource<Object>> resourceLiveData) {\n        final Resource<Object> resource = resourceLiveData.getValue();\n        if (resource == null) return;\n        final Resource.Status status = resource.status;\n        switch (status) {\n            case SUCCESS:\n                resourceLiveData.removeObservers(getViewLifecycleOwner());\n                break;\n            case LOADING:\n                break;\n            case ERROR:\n                if (resource.message != null) {\n                    Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();\n                }\n                if (resource.resId != 0) {\n                    Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show();\n                }\n                resourceLiveData.removeObservers(getViewLifecycleOwner());\n                break;\n        }\n    }\n\n    private void setupEmojiPicker() {\n        root.post(() -> binding.emojiPicker.init(\n                root,\n                (view, emoji) -> {\n                    final KeyNotifyingEmojiEditText input = binding.input;\n                    final int start = input.getSelectionStart();\n                    final int end = input.getSelectionEnd();\n                    if (start < 0) {\n                        input.append(emoji.getUnicode());\n                        return;\n                    }\n                    input.getText().replace(\n                            Math.min(start, end),\n                            Math.max(start, end),\n                            emoji.getUnicode(),\n                            0,\n                            emoji.getUnicode().length()\n                    );\n                },\n                () -> binding.input.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))\n        ));\n        binding.emojiToggle.setOnClickListener(v -> {\n            Boolean isEmojiPickerVisible = emojiPickerVisible.getValue();\n            if (isEmojiPickerVisible == null) isEmojiPickerVisible = false;\n            Boolean isKbVisible = kbVisible.getValue();\n            if (isKbVisible == null) isKbVisible = false;\n            wasToggled = isEmojiPickerVisible || isKbVisible;\n\n            if (isEmojiPickerVisible) {\n                if (hasKbOpenedOnce && binding.emojiPicker.getTranslationY() != 0) {\n                    inputHolderAnimationCallback.setShouldTranslate(false);\n                    chatsAnimationCallback.setShouldTranslate(false);\n                    emojiPickerAnimationCallback.setShouldTranslate(false);\n                }\n                // trigger ime.\n                // Since the kb visibility listener will toggle the emojiPickerVisible live data, we do not explicitly toggle it here\n                showKeyboard();\n                return;\n            }\n\n            if (isKbVisible) {\n                // hide the keyboard, but don't translate the views\n                // Since the kb visibility listener will toggle the emojiPickerVisible live data, we do not explicitly toggle it here\n                inputHolderAnimationCallback.setShouldTranslate(false);\n                chatsAnimationCallback.setShouldTranslate(false);\n                emojiPickerAnimationCallback.setShouldTranslate(false);\n                hideKeyboard();\n            }\n            emojiPickerVisible.postValue(true);\n        });\n        final LiveData<Pair<Boolean, Boolean>> emojiKbVisibilityLD = Utils.zipLiveData(emojiPickerVisible, kbVisible);\n        emojiKbVisibilityLD.observe(getViewLifecycleOwner(), pair -> {\n            Boolean isEmojiPickerVisible = pair.first;\n            Boolean isKbVisible = pair.second;\n            if (isEmojiPickerVisible == null) isEmojiPickerVisible = false;\n            if (isKbVisible == null) isKbVisible = false;\n            root.setScrollImeOffScreenWhenVisible(!isEmojiPickerVisible);\n            root.setScrollImeOnScreenWhenNotVisible(!isEmojiPickerVisible);\n            onEmojiPickerBackPressedCallback.setEnabled(isEmojiPickerVisible && !isKbVisible);\n            if (isEmojiPickerVisible && !isKbVisible) {\n                animatePan(binding.emojiPicker.getMeasuredHeight(), unused -> {\n                    binding.emojiPicker.setAlpha(1);\n                    binding.emojiToggle.setIconResource(R.drawable.ic_keyboard_24);\n                    return null;\n                }, null);\n                return;\n            }\n            if (!isEmojiPickerVisible && !isKbVisible) {\n                animatePan(0, null, unused -> {\n                    binding.emojiPicker.setAlpha(0);\n                    binding.emojiToggle.setIconResource(R.drawable.ic_face_24);\n                    return null;\n                });\n                return;\n            }\n            // isKbVisible will always be true going forward\n            hasKbOpenedOnce = true;\n            if (!isEmojiPickerVisible) {\n                binding.emojiToggle.setIconResource(R.drawable.ic_face_24);\n                binding.emojiPicker.setAlpha(0);\n                return;\n            }\n            binding.emojiPicker.setAlpha(1);\n        });\n    }\n\n    public void showKeyboard() {\n        final Context context = getContext();\n        if (context == null) return;\n        final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) return;\n        if (!binding.input.isFocused()) {\n            binding.input.requestFocus();\n        }\n        final boolean shown = imm.showSoftInput(binding.input, InputMethodManager.SHOW_IMPLICIT);\n        if (!shown) {\n            Log.e(TAG, \"showKeyboard: System did not display the keyboard\");\n        }\n    }\n\n    public void hideKeyboard() {\n        final Context context = getContext();\n        if (context == null) return;\n        final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) return;\n        imm.hideSoftInputFromWindow(binding.input.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);\n    }\n\n    private void setSendToMicIcon() {\n        final Context context = getContext();\n        if (context == null) return;\n        final Drawable sendToMicDrawable = Utils.getAnimatableDrawable(context, R.drawable.avd_send_to_mic_anim);\n        if (sendToMicDrawable instanceof Animatable) {\n            AnimatedVectorDrawableCompat.registerAnimationCallback(sendToMicDrawable, sendToMicAnimationCallback);\n        }\n        binding.send.setIcon(sendToMicDrawable);\n    }\n\n    private void setMicToSendIcon() {\n        final Context context = getContext();\n        if (context == null) return;\n        final Drawable micToSendDrawable = Utils.getAnimatableDrawable(context, R.drawable.avd_mic_to_send_anim);\n        if (micToSendDrawable instanceof Animatable) {\n            AnimatedVectorDrawableCompat.registerAnimationCallback(micToSendDrawable, micToSendAnimationCallback);\n        }\n        binding.send.setIcon(micToSendDrawable);\n    }\n\n    private void setTitle(final String title) {\n        if (actionBar == null) return;\n        if (prevTitleRunnable != null) {\n            appExecutors.getMainThread().cancel(prevTitleRunnable);\n        }\n        prevTitleRunnable = () -> actionBar.setTitle(title);\n        // set title delayed to avoid title blink if fetch is fast\n        appExecutors.getMainThread().execute(prevTitleRunnable, 1000);\n    }\n\n    private void downloadItem(final DirectItem item) {\n        final Context context = getContext();\n        if (context == null) return;\n        final DirectItemType itemType = item.getItemType();\n        if (itemType == null) return;\n        //noinspection SwitchStatementWithTooFewBranches\n        switch (itemType) {\n            case VOICE_MEDIA:\n                downloadItem(context, item.getVoiceMedia() == null ? null : item.getVoiceMedia().getMedia());\n                break;\n            default:\n                break;\n        }\n    }\n\n    // currently ONLY for voice\n    private void downloadItem(@NonNull final Context context, final Media media) {\n        if (media == null) {\n            Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();\n            return;\n        }\n        DownloadUtils.download(context, media);\n        Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();\n    }\n\n    // Sets the translationY of views to height with animation\n    private void animatePan(final int height,\n                            @Nullable final Function<Void, Void> onAnimationStart,\n                            @Nullable final Function<Void, Void> onAnimationEnd) {\n        if (animatorSet != null && animatorSet.isStarted()) {\n            animatorSet.cancel();\n        }\n        final ImmutableList.Builder<Animator> builder = ImmutableList.builder();\n        builder.add(\n                ObjectAnimator.ofFloat(binding.chats, TRANSLATION_Y, -height),\n                ObjectAnimator.ofFloat(binding.inputHolder, TRANSLATION_Y, -height),\n                ObjectAnimator.ofFloat(binding.emojiPicker, TRANSLATION_Y, -height)\n        );\n        // if (headerItemDecoration != null && headerItemDecoration.getCurrentHeader() != null) {\n        //     builder.add(ObjectAnimator.ofFloat(headerItemDecoration.getCurrentHeader(), TRANSLATION_Y, height));\n        // }\n        animatorSet = new AnimatorSet();\n        animatorSet.playTogether(builder.build());\n        animatorSet.setDuration(200);\n        animatorSet.setInterpolator(CubicBezierInterpolator.EASE_IN);\n        animatorSet.addListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationStart(final Animator animation) {\n                super.onAnimationStart(animation);\n                if (onAnimationStart != null) {\n                    onAnimationStart.apply(null);\n                }\n            }\n\n            @Override\n            public void onAnimationEnd(final Animator animation) {\n                super.onAnimationEnd(animation);\n                animatorSet = null;\n                if (onAnimationEnd != null) {\n                    onAnimationEnd.apply(null);\n                }\n            }\n        });\n        animatorSet.start();\n    }\n\n    private void showReactionsDialog(final DirectItem item) {\n        final LiveData<List<User>> users = viewModel.getUsers();\n        final LiveData<List<User>> leftUsers = viewModel.getLeftUsers();\n        final ArrayList<User> allUsers = new ArrayList<>();\n        allUsers.add(viewModel.getCurrentUser());\n        if (users.getValue() != null) {\n            allUsers.addAll(users.getValue());\n        }\n        if (leftUsers.getValue() != null) {\n            allUsers.addAll(leftUsers.getValue());\n        }\n        final String itemId = item.getItemId();\n        if (itemId == null) return;\n        final DirectItemReactions reactions = item.getReactions();\n        if (reactions == null) return;\n        reactionDialogFragment = DirectItemReactionDialogFragment.newInstance(\n                viewModel.getViewerId(),\n                allUsers,\n                itemId,\n                reactions\n        );\n        reactionDialogFragment.show(getChildFragmentManager(), \"reactions_dialog\");\n    }\n\n    @Override\n    public void onReactionClick(final String itemId, final DirectItemEmojiReaction reaction) {\n        if (reactionDialogFragment != null) {\n            reactionDialogFragment.dismiss();\n        }\n        if (itemId == null || reaction == null) return;\n        if (reaction.getSenderId() == viewModel.getViewerId()) {\n            final LiveData<Resource<Object>> resourceLiveData = viewModel.sendDeleteReaction(itemId);\n            resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));\n            return;\n        }\n        // navigate to user\n        final User user = viewModel.getUser(reaction.getSenderId());\n        if (user == null) return;\n        navigateToUser(user.getUsername());\n    }\n\n    private void navigateToUser(@NonNull final String username) {\n        try {\n            final NavDirections direction = DirectMessageThreadFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(direction);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToUser: \", e);\n        }\n    }\n\n    @Override\n    public void onClick(final View view, final Emoji emoji) {\n        if (addReactionItem == null || emoji == null) return;\n        final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(addReactionItem, emoji);\n        resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.kt",
    "content": "package awais.instagrabber.fragments.directmessages\n\nimport android.content.res.Configuration\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.activityViewModels\nimport androidx.lifecycle.Observer\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener\nimport awais.instagrabber.activities.MainActivity\nimport awais.instagrabber.adapters.DirectMessageInboxAdapter\nimport awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge\nimport awais.instagrabber.databinding.FragmentDirectPendingInboxBinding\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.directmessages.DirectInbox\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\nimport awais.instagrabber.viewmodels.DirectPendingInboxViewModel\nimport com.google.android.material.snackbar.Snackbar\n\nclass DirectPendingInboxFragment : Fragment(), OnRefreshListener {\n    private val viewModel: DirectPendingInboxViewModel by activityViewModels()\n\n    private lateinit var root: CoordinatorLayout\n    private lateinit var lazyLoader: RecyclerLazyLoaderAtEdge\n    private lateinit var binding: FragmentDirectPendingInboxBinding\n    private lateinit var fragmentActivity: MainActivity\n    private lateinit var inboxAdapter: DirectMessageInboxAdapter\n\n    private var shouldRefresh = true\n    private var scrollToTop = false\n    private var navigating = false\n    private var threadsObserver: Observer<List<DirectThread?>>? = null\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        fragmentActivity = requireActivity() as MainActivity\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?,\n    ): View {\n        if (this::root.isInitialized) {\n            shouldRefresh = false\n            return root\n        }\n        binding = FragmentDirectPendingInboxBinding.inflate(inflater, container, false)\n        root = binding.root\n        return root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        if (!shouldRefresh) return\n        init()\n    }\n\n    override fun onRefresh() {\n        lazyLoader.resetState()\n        scrollToTop = true\n        viewModel.refresh()\n    }\n\n    override fun onResume() {\n        super.onResume()\n        setupObservers()\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        init()\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        removeViewModelObservers()\n        viewModel.onDestroy()\n    }\n\n    private fun setupObservers() {\n        removeViewModelObservers()\n        threadsObserver = Observer { list: List<DirectThread?>? ->\n            if (!this::inboxAdapter.isInitialized) return@Observer\n            if (binding.swipeRefreshLayout.visibility == View.GONE) {\n                binding.swipeRefreshLayout.visibility = View.VISIBLE\n                binding.empty.visibility = View.GONE\n            }\n            inboxAdapter.submitList(list ?: emptyList()) {\n                if (!scrollToTop) return@submitList\n                binding.pendingInboxList.smoothScrollToPosition(0)\n                scrollToTop = false\n            }\n            if (list == null || list.isEmpty()) {\n                binding.swipeRefreshLayout.visibility = View.GONE\n                binding.empty.visibility = View.VISIBLE\n            }\n        }\n        threadsObserver?.let { viewModel.threads.observe(fragmentActivity, it) }\n        viewModel.inbox.observe(viewLifecycleOwner, { inboxResource: Resource<DirectInbox?>? ->\n            if (inboxResource == null) return@observe\n            when (inboxResource.status) {\n                Resource.Status.SUCCESS -> binding.swipeRefreshLayout.isRefreshing = false\n                Resource.Status.ERROR -> {\n                    if (inboxResource.message != null) {\n                        Snackbar.make(binding.root, inboxResource.message, Snackbar.LENGTH_LONG).show()\n                    }\n                    binding.swipeRefreshLayout.isRefreshing = false\n                }\n                Resource.Status.LOADING -> binding.swipeRefreshLayout.isRefreshing = true\n            }\n        })\n    }\n\n    private fun removeViewModelObservers() {\n        threadsObserver?.let { viewModel.threads.removeObserver(it) }\n    }\n\n    private fun init() {\n        val context = context ?: return\n        setupObservers()\n        binding.swipeRefreshLayout.setOnRefreshListener(this)\n        binding.pendingInboxList.setHasFixedSize(true)\n        binding.pendingInboxList.setItemViewCacheSize(20)\n        val layoutManager = LinearLayoutManager(context)\n        binding.pendingInboxList.layoutManager = layoutManager\n        inboxAdapter = DirectMessageInboxAdapter { thread ->\n            if (navigating) return@DirectMessageInboxAdapter\n            val threadId = thread.threadId ?: return@DirectMessageInboxAdapter\n            val threadTitle = thread.threadTitle ?: return@DirectMessageInboxAdapter\n            navigating = true\n            if (isAdded) {\n                val directions = DirectPendingInboxFragmentDirections.actionToThread(threadId, threadTitle)\n                directions.pending = true\n                NavHostFragment.findNavController(this).navigate(directions)\n            }\n            navigating = false\n        }\n        inboxAdapter.setHasStableIds(true)\n        binding.pendingInboxList.adapter = inboxAdapter\n        lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() }\n        binding.pendingInboxList.addOnScrollListener(lazyLoader)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/FiltersFragment.java",
    "content": "package awais.instagrabber.fragments.imageedit;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.constraintlayout.widget.Barrier;\nimport androidx.constraintlayout.widget.ConstraintLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.recyclerview.widget.SimpleItemAnimator;\n\nimport com.google.android.material.slider.Slider;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.adapters.FiltersAdapter;\nimport awais.instagrabber.databinding.FragmentFiltersBinding;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper.FilterType;\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport awais.instagrabber.fragments.imageedit.filters.filters.FilterFactory;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.BitmapUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.SerializablePair;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.FiltersFragmentViewModel;\nimport awais.instagrabber.viewmodels.ImageEditViewModel;\nimport jp.co.cyberagent.android.gpuimage.GPUImage;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class FiltersFragment extends Fragment {\n    private static final String TAG = FiltersFragment.class.getSimpleName();\n\n    private static final String ARGS_SOURCE_URI = \"source_uri\";\n    private static final String ARGS_DEST_URI = \"dest_uri\";\n    private static final String ARGS_TUNING_FILTERS = \"tuning_filters\";\n    private static final String ARGS_FILTER = \"filter\";\n    private static final String ARGS_TAB = \"tab\";\n\n    private final Map<FilterType, Filter<?>> tuningFilters = new HashMap<>();\n    private final Map<Property<?>, Integer> propertySliderIdMap = new HashMap<>();\n\n    private GPUImageFilterGroup filterGroup;\n    private Filter<? extends GPUImageFilter> appliedFilter;\n    private FragmentFiltersBinding binding;\n    private AppExecutors appExecutors;\n    private Uri sourceUri;\n    private Uri destUri;\n    private FiltersFragmentViewModel viewModel;\n    private boolean isFilterGroupSet = false;\n    private FilterCallback callback;\n    private FiltersAdapter filtersAdapter;\n    private HashMap<FilterType, Map<Integer, Object>> initialTuningFiltersValues;\n    private SerializablePair<FilterType, Map<Integer, Object>> initialFilter;\n\n    @NonNull\n    public static FiltersFragment newInstance(@NonNull final Uri sourceUri,\n                                              @NonNull final Uri destUri,\n                                              @NonNull final ImageEditViewModel.Tab tab) {\n        return newInstance(sourceUri, destUri, null, null, tab);\n    }\n\n    @NonNull\n    public static FiltersFragment newInstance(@NonNull final Uri sourceUri,\n                                              @NonNull final Uri destUri,\n                                              final HashMap<FilterType, Map<Integer, Object>> appliedTuningFilters,\n                                              final SerializablePair<FilterType, Map<Integer, Object>> appliedFilter,\n                                              @NonNull final ImageEditViewModel.Tab tab) {\n        final Bundle args = new Bundle();\n        args.putParcelable(ARGS_SOURCE_URI, sourceUri);\n        args.putParcelable(ARGS_DEST_URI, destUri);\n        if (appliedTuningFilters != null) {\n            args.putSerializable(ARGS_TUNING_FILTERS, appliedTuningFilters);\n        }\n        if (appliedFilter != null) {\n            args.putSerializable(ARGS_FILTER, appliedFilter);\n        }\n        args.putString(ARGS_TAB, tab.name());\n        final FiltersFragment fragment = new FiltersFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public FiltersFragment() {\n        filterGroup = new GPUImageFilterGroup();\n        filterGroup.addFilter(new GPUImageFilter());\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        appExecutors = AppExecutors.INSTANCE;\n        viewModel = new ViewModelProvider(this).get(FiltersFragmentViewModel.class);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = FragmentFiltersBinding.inflate(inflater, container, false);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init(savedInstanceState);\n    }\n\n    @Override\n    public void onSaveInstanceState(@NonNull final Bundle outState) {\n        super.onSaveInstanceState(outState);\n        final ImageEditViewModel.Tab tab = viewModel.getCurrentTab().getValue();\n        if (tab != null) {\n            outState.putString(ARGS_TAB, tab.name());\n        }\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        // binding.preview.onPause();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        // binding.preview.onResume();\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        for (final GPUImageFilter filter : filterGroup.getFilters()) {\n            filter.destroy();\n        }\n        filterGroup.getFilters().clear();\n        filterGroup.destroy();\n    }\n\n    private void init(final Bundle savedInstanceState) {\n        setupObservers();\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final Parcelable uriParcelable = arguments.getParcelable(ARGS_SOURCE_URI);\n        if (!(uriParcelable instanceof Uri)) return;\n        sourceUri = (Uri) uriParcelable;\n        final Parcelable destUriParcelable = arguments.getParcelable(ARGS_DEST_URI);\n        if (!(destUriParcelable instanceof Uri)) return;\n        destUri = (Uri) destUriParcelable;\n        final Serializable tuningFiltersSerializable = arguments.getSerializable(ARGS_TUNING_FILTERS);\n        if (tuningFiltersSerializable instanceof HashMap) {\n            try {\n                //noinspection unchecked\n                initialTuningFiltersValues = (HashMap<FilterType, Map<Integer, Object>>) tuningFiltersSerializable;\n            } catch (Exception e) {\n                Log.e(TAG, \"init: \", e);\n            }\n        }\n        final Serializable filterSerializable = arguments.getSerializable(ARGS_FILTER);\n        if (filterSerializable instanceof SerializablePair) {\n            try {\n                //noinspection unchecked\n                initialFilter = (SerializablePair<FilterType, Map<Integer, Object>>) filterSerializable;\n            } catch (Exception e) {\n                Log.e(TAG, \"init: \", e);\n            }\n        }\n        final Context context = getContext();\n        if (context == null) return;\n        binding.preview.setScaleType(GPUImage.ScaleType.CENTER_INSIDE);\n        appExecutors.getTasksThread().execute(() -> {\n            binding.preview.setImage(sourceUri);\n            setPreviewBounds();\n        });\n        setCurrentTab(ImageEditViewModel.Tab.valueOf(savedInstanceState != null && savedInstanceState.containsKey(ARGS_TAB)\n                                                     ? savedInstanceState.getString(ARGS_TAB)\n                                                     : arguments.getString(ARGS_TAB)));\n        binding.cancel.setOnClickListener(v -> {\n            if (callback == null) return;\n            callback.onCancel();\n        });\n        binding.reset.setOnClickListener(v -> {\n            final ImageEditViewModel.Tab tab = viewModel.getCurrentTab().getValue();\n            if (tab == ImageEditViewModel.Tab.TUNE) {\n                final Collection<Filter<?>> filters = tuningFilters.values();\n                for (final Filter<?> filter : filters) {\n                    if (filter == null) continue;\n                    filter.reset();\n                }\n                resetSliders();\n            }\n            if (tab == ImageEditViewModel.Tab.FILTERS) {\n                final List<GPUImageFilter> groupFilters = filterGroup.getFilters();\n                if (appliedFilter != null) {\n                    groupFilters.remove(appliedFilter.getInstance());\n                    appliedFilter = null;\n                }\n                if (filtersAdapter != null) {\n                    filtersAdapter.setSelected(0);\n                }\n                binding.preview.post(() -> binding.preview.setFilter(filterGroup = new GPUImageFilterGroup(groupFilters)));\n            }\n        });\n        binding.apply.setOnClickListener(v -> {\n            if (callback == null) return;\n            final List<Filter<?>> appliedTunings = getAppliedTunings();\n            appExecutors.getTasksThread().submit(() -> {\n                final Bitmap bitmap = binding.preview.getGPUImage().getBitmapWithFilterApplied();\n                try {\n                    BitmapUtils.convertToJpegAndSaveToUri(context, bitmap, destUri);\n                    callback.onApply(destUri, appliedTunings, appliedFilter);\n                } catch (Exception e) {\n                    Log.e(TAG, \"init: \", e);\n                }\n            });\n        });\n    }\n\n    @NonNull\n    private List<Filter<?>> getAppliedTunings() {\n        return tuningFilters\n                .values()\n                .stream()\n                .filter(Objects::nonNull)\n                .filter(filter -> {\n                    final Map<Integer, Property<?>> propertyMap = filter.getProperties();\n                    if (propertyMap == null) return false;\n                    final Collection<Property<?>> properties = propertyMap.values();\n                    return properties.stream()\n                                     .noneMatch(property -> {\n                                         final Object value = property.getValue();\n                                         if (value == null) {\n                                             return false;\n                                         }\n                                         return value.equals(property.getDefaultValue());\n                                     });\n                })\n                .collect(Collectors.toList());\n    }\n\n    private void resetSliders() {\n        final Set<Map.Entry<Property<?>, Integer>> entries = propertySliderIdMap.entrySet();\n        for (final Map.Entry<Property<?>, Integer> entry : entries) {\n            final Property<?> property = entry.getKey();\n            final Integer viewId = entry.getValue();\n            final Slider slider = (Slider) binding.getRoot().findViewById(viewId);\n            if (slider == null) continue;\n            final Object defaultValue = property.getDefaultValue();\n            if (!(defaultValue instanceof Float)) continue;\n            slider.setValue((float) defaultValue);\n        }\n    }\n\n    private void setPreviewBounds() {\n        InputStream inputStream = null;\n        try {\n            final BitmapFactory.Options options = new BitmapFactory.Options();\n            options.inJustDecodeBounds = true;\n            final Context context = getContext();\n            if (context == null) return;\n            inputStream = context.getContentResolver().openInputStream(sourceUri);\n            BitmapFactory.decodeStream(inputStream, null, options);\n            final float ratio = (float) options.outWidth / options.outHeight;\n            appExecutors.getMainThread().execute(() -> {\n                final ViewGroup.LayoutParams previewLayoutParams = binding.preview.getLayoutParams();\n                if (options.outHeight > options.outWidth) {\n                    previewLayoutParams.width = (int) (binding.preview.getHeight() * ratio);\n                } else {\n                    previewLayoutParams.height = (int) (binding.preview.getWidth() / ratio);\n                }\n                binding.preview.setRatio(ratio);\n                binding.preview.requestLayout();\n            });\n        } catch (FileNotFoundException e) {\n            Log.e(TAG, \"setPreviewBounds: \", e);\n        } finally {\n            if (inputStream != null) {\n                try {\n                    inputStream.close();\n                } catch (IOException ignored) {}\n            }\n        }\n    }\n\n    private void setupObservers() {\n        viewModel.isLoading().observe(getViewLifecycleOwner(), loading -> {\n\n        });\n        viewModel.getCurrentTab().observe(getViewLifecycleOwner(), tab -> {\n            switch (tab) {\n                case TUNE:\n                    setupTuning();\n                    break;\n                case FILTERS:\n                    setupFilters();\n                    break;\n                default:\n                    break;\n            }\n        });\n    }\n\n    private void setupTuning() {\n        initTuningControls();\n        binding.filters.setVisibility(View.GONE);\n        binding.tuneControlsWrapper.setVisibility(View.VISIBLE);\n    }\n\n    private void initTuningControls() {\n        final Context context = getContext();\n        if (context == null) return;\n        final ConstraintLayout controlsParent = new ConstraintLayout(context);\n        controlsParent.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        final Barrier sliderBarrier = new Barrier(context);\n        sliderBarrier.setId(Barrier.generateViewId());\n        sliderBarrier.setType(Barrier.START);\n        controlsParent.addView(sliderBarrier);\n        binding.tuneControlsWrapper.addView(controlsParent);\n        final int labelPadding = Utils.convertDpToPx(8);\n        final List<Filter<?>> tuneFilters = FiltersHelper.getTuneFilters();\n        Slider previousSlider = null;\n        // Need to iterate backwards\n        for (int i = tuneFilters.size() - 1; i >= 0; i--) {\n            final Filter<?> tuneFilter = tuneFilters.get(i);\n            if (tuneFilter.getProperties() == null || tuneFilter.getProperties().isEmpty() || tuneFilter.getProperties().size() > 1) continue;\n            final int propKey = tuneFilter.getProperties().keySet().iterator().next();\n            final Property<?> property = tuneFilter.getProperties().values().iterator().next();\n            if (!(property instanceof FloatProperty)) continue;\n            final GPUImageFilter filterInstance = tuneFilter.getInstance();\n            tuningFilters.put(tuneFilter.getType(), tuneFilter);\n            filterGroup.addFilter(filterInstance);\n\n            final FloatProperty floatProperty = (FloatProperty) property;\n            final Slider slider = new Slider(context);\n            final int viewId = Slider.generateViewId();\n            slider.setId(viewId);\n            propertySliderIdMap.put(floatProperty, viewId);\n\n            final ConstraintLayout.LayoutParams sliderLayoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,\n                                                                                                       ConstraintLayout.LayoutParams.WRAP_CONTENT);\n\n            sliderLayoutParams.startToEnd = sliderBarrier.getId();\n            sliderLayoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;\n            if (previousSlider == null) {\n                sliderLayoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;\n            } else {\n                sliderLayoutParams.bottomToTop = previousSlider.getId();\n                final ConstraintLayout.LayoutParams prevSliderLayoutParams = (ConstraintLayout.LayoutParams) previousSlider.getLayoutParams();\n                prevSliderLayoutParams.topToBottom = slider.getId();\n            }\n            if (i == 0) {\n                sliderLayoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;\n            }\n            slider.setLayoutParams(sliderLayoutParams);\n            slider.setValueFrom(floatProperty.getMinValue());\n            slider.setValueTo(floatProperty.getMaxValue());\n            float defaultValue = floatProperty.getDefaultValue();\n            if (initialTuningFiltersValues != null && initialTuningFiltersValues.containsKey(tuneFilter.getType())) {\n                final Map<Integer, Object> valueMap = initialTuningFiltersValues.get(tuneFilter.getType());\n                if (valueMap != null) {\n                    final Object value = valueMap.get(propKey);\n                    if (value instanceof Float) {\n                        defaultValue = (float) value;\n                        tuneFilter.adjust(propKey, value);\n                    }\n                }\n            }\n            slider.setValue(defaultValue);\n            slider.addOnChangeListener((slider1, value, fromUser) -> {\n                final Filter<?> filter = tuningFilters.get(tuneFilter.getType());\n                if (filter != null) {\n                    tuneFilter.adjust(propKey, value);\n                }\n                binding.preview.post(() -> binding.preview.requestRender());\n            });\n\n            final AppCompatTextView label = new AppCompatTextView(context);\n            label.setId(AppCompatTextView.generateViewId());\n            final ConstraintLayout.LayoutParams labelLayoutParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,\n                                                                                                      ConstraintLayout.LayoutParams.MATCH_CONSTRAINT);\n            labelLayoutParams.topToTop = slider.getId();\n            labelLayoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;\n            labelLayoutParams.endToStart = sliderBarrier.getId();\n            labelLayoutParams.bottomToBottom = slider.getId();\n            labelLayoutParams.horizontalBias = 1;\n            label.setLayoutParams(labelLayoutParams);\n            label.setGravity(Gravity.CENTER);\n            label.setPadding(labelPadding, labelPadding, labelPadding, labelPadding);\n            label.setText(tuneFilter.getLabel());\n\n            controlsParent.addView(label);\n            controlsParent.addView(slider);\n\n            previousSlider = slider;\n        }\n        addInitialFilter();\n        if (!isFilterGroupSet) {\n            isFilterGroupSet = true;\n            binding.preview.post(() -> binding.preview.setFilter(filterGroup));\n        }\n    }\n\n    private void addInitialFilter() {\n        if (initialFilter == null) return;\n        final Filter<?> instance = FilterFactory.getInstance(initialFilter.first);\n        if (instance == null) return;\n        addFilterToGroup(instance, initialFilter.second);\n        appliedFilter = instance;\n    }\n\n    private void setupFilters() {\n        final Context context = getContext();\n        if (context == null) return;\n        addTuneFilters();\n        binding.filters.setVisibility(View.VISIBLE);\n        final RecyclerView.ItemAnimator animator = binding.filters.getItemAnimator();\n        if (animator instanceof SimpleItemAnimator) {\n            final SimpleItemAnimator itemAnimator = (SimpleItemAnimator) animator;\n            itemAnimator.setSupportsChangeAnimations(false);\n        }\n        binding.tuneControlsWrapper.setVisibility(View.GONE);\n        binding.filters.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));\n        final FiltersAdapter.OnFilterClickListener onFilterClickListener = (position, filter) -> {\n            if (appliedFilter != null && appliedFilter.equals(filter)) return;\n            final List<GPUImageFilter> filters = filterGroup.getFilters();\n            if (appliedFilter != null) {\n                // remove applied filter from current filter list\n                filters.remove(appliedFilter.getInstance());\n            }\n            // add the new filter\n            filters.add(filter.getInstance());\n            filterGroup = new GPUImageFilterGroup(filters);\n            binding.preview.post(() -> binding.preview.setFilter(filterGroup));\n            filtersAdapter.setSelected(position);\n            appliedFilter = filter;\n        };\n        BitmapUtils.getThumbnail(\n                context,\n                sourceUri,\n                CoroutineUtilsKt.getContinuation((bitmapResult, throwable) -> appExecutors.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"setupFilters: \", throwable);\n                        return;\n                    }\n                    if (bitmapResult == null || bitmapResult.getBitmap() == null) {\n                        return;\n                    }\n                    filtersAdapter = new FiltersAdapter(\n                            tuningFilters.values()\n                                         .stream()\n                                         .map(Filter::getInstance)\n                                         .collect(Collectors.toList()),\n                            sourceUri.toString(),\n                            bitmapResult.getBitmap(),\n                            onFilterClickListener\n                    );\n                    binding.filters.setAdapter(filtersAdapter);\n                    filtersAdapter.submitList(FiltersHelper.getFilters(), () -> {\n                        if (appliedFilter == null) return;\n                        filtersAdapter.setSelectedFilter(appliedFilter.getInstance());\n                    });\n                }), Dispatchers.getIO())\n        );\n        addInitialFilter();\n        binding.preview.setFilter(filterGroup);\n    }\n\n    private void addTuneFilters() {\n        if (initialTuningFiltersValues == null) return;\n        final List<Filter<?>> tuneFilters = FiltersHelper.getTuneFilters();\n        for (final Filter<?> tuneFilter : tuneFilters) {\n            if (!initialTuningFiltersValues.containsKey(tuneFilter.getType())) continue;\n            addFilterToGroup(tuneFilter, initialTuningFiltersValues.get(tuneFilter.getType()));\n        }\n    }\n\n    private void addFilterToGroup(@NonNull final Filter<?> tuneFilter, final Map<Integer, Object> valueMap) {\n        final GPUImageFilter filter = tuneFilter.getInstance();\n        filterGroup.addFilter(filter);\n        if (valueMap == null) return;\n        final Set<Map.Entry<Integer, Object>> entries = valueMap.entrySet();\n        for (final Map.Entry<Integer, Object> entry : entries) {\n            tuneFilter.adjust(entry.getKey(), entry.getValue());\n        }\n    }\n\n    public void setCurrentTab(final ImageEditViewModel.Tab tab) {\n        viewModel.setCurrentTab(tab);\n    }\n\n    public void setCallback(final FilterCallback callback) {\n        if (callback == null) return;\n        this.callback = callback;\n    }\n\n    public interface FilterCallback {\n        void onApply(final Uri uri, List<Filter<?>> tuningFilters, Filter<?> filter);\n\n        void onCancel();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/ImageEditFragment.java",
    "content": "package awais.instagrabber.fragments.imageedit;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.RectF;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.lifecycle.SavedStateHandle;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavBackStackEntry;\nimport androidx.navigation.NavController;\nimport androidx.navigation.fragment.NavHostFragment;\n\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.facebook.imagepipeline.request.ImageRequestBuilder;\nimport com.yalantis.ucrop.UCrop;\nimport com.yalantis.ucrop.UCropActivity;\nimport com.yalantis.ucrop.UCropFragment;\nimport com.yalantis.ucrop.UCropFragmentCallback;\n\nimport java.io.File;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.databinding.FragmentImageEditBinding;\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport awais.instagrabber.models.SavedImageEditState;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.ImageEditViewModel;\n\npublic class ImageEditFragment extends Fragment {\n    private static final String TAG = ImageEditFragment.class.getSimpleName();\n    private static final String ARGS_URI = \"uri\";\n    private static final String FILTERS_FRAGMENT_TAG = \"Filters\";\n\n    private FragmentImageEditBinding binding;\n    private ImageEditViewModel viewModel;\n    private ImageEditViewModel.Tab previousTab;\n    private FiltersFragment filtersFragment;\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            setEnabled(false);\n            remove();\n            if (previousTab != ImageEditViewModel.Tab.CROP\n                    && previousTab != ImageEditViewModel.Tab.TUNE\n                    && previousTab != ImageEditViewModel.Tab.FILTERS) {\n                return;\n            }\n            final FragmentManager fragmentManager = getChildFragmentManager();\n            final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();\n            fragmentTransaction.setReorderingAllowed(true)\n                               .remove(previousTab == ImageEditViewModel.Tab.CROP ? uCropFragment : filtersFragment)\n                               .commit();\n            viewModel.setCurrentTab(ImageEditViewModel.Tab.RESULT);\n        }\n    };\n    private FragmentActivity fragmentActivity;\n    private UCropFragment uCropFragment;\n\n    public static ImageEditFragment newInstance(final Uri uri) {\n        final Bundle args = new Bundle();\n        args.putParcelable(ARGS_URI, uri);\n        final ImageEditFragment fragment = new ImageEditFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public ImageEditFragment() {}\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = getActivity();\n        viewModel = new ViewModelProvider(this).get(ImageEditViewModel.class);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        binding = FragmentImageEditBinding.inflate(inflater, container, false);\n        return binding.getRoot();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        init();\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n    }\n\n    private void init() {\n        setupObservers();\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final Parcelable parcelable = arguments.getParcelable(ARGS_URI);\n        Uri originalUri = null;\n        if (parcelable instanceof Uri) {\n            originalUri = (Uri) parcelable;\n        }\n        if (originalUri == null) return;\n        viewModel.setOriginalUri(originalUri);\n        viewModel.setCurrentTab(ImageEditViewModel.Tab.RESULT);\n    }\n\n    private void setupObservers() {\n        viewModel.isLoading().observe(getViewLifecycleOwner(), loading -> {});\n        viewModel.getCurrentTab().observe(getViewLifecycleOwner(), tab -> {\n            if (tab == null) return;\n            switch (tab) {\n                case RESULT:\n                    setupResult();\n                    break;\n                case CROP:\n                    setupCropFragment();\n                    break;\n                case TUNE:\n                case FILTERS:\n                    setupFilterFragment();\n                    break;\n            }\n            previousTab = tab;\n        });\n        viewModel.isCropped().observe(getViewLifecycleOwner(), isCropped -> binding.crop.setSelected(isCropped));\n        viewModel.isTuned().observe(getViewLifecycleOwner(), isTuned -> binding.tune.setSelected(isTuned));\n        viewModel.isFiltered().observe(getViewLifecycleOwner(), isFiltered -> binding.filters.setSelected(isFiltered));\n        viewModel.getResultUri().observe(getViewLifecycleOwner(), uri -> {\n            if (uri == null) {\n                binding.preview.setController(null);\n                return;\n            }\n            binding.preview.setController(Fresco.newDraweeControllerBuilder()\n                                                .setImageRequest(ImageRequestBuilder.newBuilderWithSource(uri)\n                                                                                    .disableDiskCache()\n                                                                                    .disableMemoryCache()\n                                                                                    .build())\n                                                .build());\n        });\n    }\n\n    private void setupResult() {\n        binding.fragmentContainerView.setVisibility(View.GONE);\n        binding.cropBottomControls.setVisibility(View.GONE);\n        binding.preview.setVisibility(View.VISIBLE);\n        binding.resultBottomControls.setVisibility(View.VISIBLE);\n        binding.crop.setOnClickListener(v -> viewModel.setCurrentTab(ImageEditViewModel.Tab.CROP));\n        binding.tune.setOnClickListener(v -> viewModel.setCurrentTab(ImageEditViewModel.Tab.TUNE));\n        binding.filters.setOnClickListener(v -> viewModel.setCurrentTab(ImageEditViewModel.Tab.FILTERS));\n        binding.cancel.setOnClickListener(v -> {\n            viewModel.cancel();\n            final NavController navController = NavHostFragment.findNavController(this);\n            setNavControllerResult(navController, null);\n            navController.navigateUp();\n        });\n        binding.done.setOnClickListener(v -> {\n            final Context context = getContext();\n            if (context == null) return;\n            final Uri resultUri = viewModel.getResultUri().getValue();\n            if (resultUri == null) return;\n            AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                final NavController navController = NavHostFragment.findNavController(this);\n                setNavControllerResult(navController, resultUri);\n                navController.navigateUp();\n            });\n            // Utils.mediaScanFile(context, new File(resultUri.toString()), (path, uri) -> );\n        });\n    }\n\n    private void setNavControllerResult(@NonNull final NavController navController, final Uri resultUri) {\n        final NavBackStackEntry navBackStackEntry = navController.getPreviousBackStackEntry();\n        if (navBackStackEntry == null) return;\n        final SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();\n        savedStateHandle.set(\"result\", resultUri);\n    }\n\n    private void setupCropFragment() {\n        final Context context = getContext();\n        if (context == null) return;\n        binding.preview.setVisibility(View.GONE);\n        binding.resultBottomControls.setVisibility(View.GONE);\n        binding.fragmentContainerView.setVisibility(View.VISIBLE);\n        binding.cropBottomControls.setVisibility(View.VISIBLE);\n        final UCrop.Options options = new UCrop.Options();\n        options.setCompressionFormat(Bitmap.CompressFormat.JPEG);\n        options.setFreeStyleCropEnabled(true);\n        options.setAllowedGestures(UCropActivity.SCALE, UCropActivity.ROTATE, UCropActivity.ALL);\n        final UCrop uCrop = UCrop.of(viewModel.getOriginalUri(), viewModel.getCropDestinationUri()).withOptions(options);\n        final SavedImageEditState savedState = viewModel.getSavedImageEditState();\n        if (savedState != null && savedState.getCropImageMatrixValues() != null && savedState.getCropRect() != null) {\n            uCrop.withSavedState(savedState.getCropImageMatrixValues(), savedState.getCropRect());\n        }\n        uCropFragment = uCrop.getFragment(uCrop.getIntent(context).getExtras());\n        final FragmentManager fragmentManager = getChildFragmentManager();\n        uCropFragment.setCallback(new UCropFragmentCallback() {\n            @Override\n            public void loadingProgress(final boolean showLoader) {\n                Log.d(TAG, \"loadingProgress: \" + showLoader);\n            }\n\n            @Override\n            public void onCropFinish(final UCropFragment.UCropResult result) {\n                Log.d(TAG, \"onCropFinish: \" + result.mResultCode);\n                if (result.mResultCode == AppCompatActivity.RESULT_OK) {\n                    final Intent resultData = result.mResultData;\n                    final Bundle extras = resultData.getExtras();\n                    if (extras == null) return;\n                    final Object uri = extras.get(UCrop.EXTRA_OUTPUT_URI);\n                    final Object imageMatrixValues = extras.get(UCrop.EXTRA_IMAGE_MATRIX_VALUES);\n                    final Object cropRect = extras.get(UCrop.EXTRA_CROP_RECT);\n                    if (uri instanceof Uri && imageMatrixValues instanceof float[] && cropRect instanceof RectF) {\n                        Log.d(TAG, \"onCropFinish: result uri: \" + uri);\n                        viewModel.setCropResult((float[]) imageMatrixValues, (RectF) cropRect);\n                        viewModel.setCurrentTab(ImageEditViewModel.Tab.RESULT);\n                    }\n                }\n            }\n        });\n        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();\n        fragmentTransaction.setReorderingAllowed(true)\n                           .replace(R.id.fragment_container_view, uCropFragment, UCropFragment.TAG)\n                           .commit();\n        if (!onBackPressedCallback.isEnabled()) {\n            final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n            onBackPressedCallback.setEnabled(true);\n            onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n        }\n        binding.cropCancel.setOnClickListener(v -> onBackPressedCallback.handleOnBackPressed());\n        binding.cropReset.setOnClickListener(v -> uCropFragment.reset());\n        binding.cropDone.setOnClickListener(v -> uCropFragment.cropAndSaveImage());\n    }\n\n    private void setupFilterFragment() {\n        binding.resultBottomControls.setVisibility(View.GONE);\n        binding.preview.setVisibility(View.GONE);\n        binding.cropBottomControls.setVisibility(View.GONE);\n        binding.fragmentContainerView.setVisibility(View.VISIBLE);\n        final Boolean isCropped = viewModel.isCropped().getValue();\n        final Uri uri = isCropped != null && isCropped ? viewModel.getCropDestinationUri() : viewModel.getOriginalUri();\n        final ImageEditViewModel.Tab value = viewModel.getCurrentTab().getValue();\n        final SavedImageEditState savedImageEditState = viewModel.getSavedImageEditState();\n        filtersFragment = FiltersFragment.newInstance(\n                uri,\n                viewModel.getDestinationUri(),\n                savedImageEditState.getAppliedTuningFilters(),\n                savedImageEditState.getAppliedFilter(),\n                value == null ? ImageEditViewModel.Tab.TUNE : value\n        );\n        final FragmentManager fragmentManager = getChildFragmentManager();\n        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();\n        fragmentTransaction.setReorderingAllowed(true)\n                           .replace(R.id.fragment_container_view, filtersFragment, FILTERS_FRAGMENT_TAG)\n                           .commit();\n        if (!onBackPressedCallback.isEnabled()) {\n            final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n            onBackPressedCallback.setEnabled(true);\n            onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n        }\n        filtersFragment.setCallback(new FiltersFragment.FilterCallback() {\n            @Override\n            public void onApply(final Uri uri, final List<Filter<?>> tuningFilters, final Filter<?> filter) {\n                viewModel.setAppliedFilters(tuningFilters, filter);\n                viewModel.setCurrentTab(ImageEditViewModel.Tab.RESULT);\n            }\n\n            @Override\n            public void onCancel() {\n                onBackPressedCallback.handleOnBackPressed();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/FiltersHelper.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport awais.instagrabber.fragments.imageedit.filters.filters.FilterFactory;\n\npublic final class FiltersHelper {\n\n    public static List<Filter<?>> getTuneFilters() {\n        return TUNING_FILTERS.stream()\n                             .map(FilterFactory::getInstance)\n                             .filter(Objects::nonNull)\n                             .collect(Collectors.toList());\n    }\n\n    public static List<Filter<?>> getFilters() {\n        // Return all non-tuning filters\n        return Arrays.stream(FilterType.values())\n                     .filter(filterType -> !TUNING_FILTERS.contains(filterType))\n                     .map(FilterFactory::getInstance)\n                     .filter(Objects::nonNull)\n                     .collect(Collectors.toList());\n    }\n\n    public static final List<FilterType> TUNING_FILTERS = ImmutableList.of(\n            FilterType.BRIGHTNESS,\n            FilterType.CONTRAST,\n            FilterType.VIBRANCE,\n            FilterType.SATURATION,\n            FilterType.SHARPEN,\n            FilterType.EXPOSURE\n    );\n\n    public enum FilterType {\n        // Tune\n        BRIGHTNESS,\n        CONTRAST,\n        VIBRANCE,\n        SATURATION,\n        SHARPEN,\n        EXPOSURE,\n\n        // Filters\n        NORMAL,\n        SEPIA,\n        CLARENDON,\n        ONE977,\n        ADEN,\n        VIGNETTE,\n        // BILATERAL_BLUR,\n        // BOX_BLUR,\n        // BULGE_DISTORTION,\n        // CGA_COLORSPACE,\n        // COLOR_BALANCE,\n        // CROSSHATCH,\n        // DILATION,\n        // EMBOSS,\n        // FALSE_COLOR,\n        // GAMMA,\n        // GAUSSIAN_BLUR,\n        // GLASS_SPHERE,\n        // GRAYSCALE,\n        // HALFTONE,\n        // HAZE,\n        // HIGHLIGHT_SHADOW,\n        // HUE,\n        // INVERT,\n        // KUWAHARA,\n        // LAPLACIAN,\n        // LEVELS_FILTER_MIN,\n        // LOOKUP_AMATORKA,\n        // LUMINANCE,\n        // LUMINANCE_THRESHOLD,\n        // MONOCHROME,\n        // NON_MAXIMUM_SUPPRESSION,\n        // OPACITY,\n        // PIXELATION,\n        // POSTERIZE,\n        // RGB,\n        // RGB_DILATION,\n        // SKETCH,\n        // SMOOTH_TOON,\n        // SOBEL_EDGE_DETECTION,\n        // SOLARIZE,\n        // SPHERE_REFRACTION,\n        // SWIRL,\n        // THREE_X_THREE_CONVOLUTION,\n        // THRESHOLD_EDGE_DETECTION,\n        // TONE_CURVE,\n        // TOON,\n        // TRANSFORM2D,\n        // WEAK_PIXEL_INCLUSION,\n        // ZOOM_BLUR\n\n        // Can be separate tunings\n        // WHITE_BALANCE,\n\n        // BLEND_ADD,\n        // BLEND_ALPHA,\n        // BLEND_CHROMA_KEY,\n        // BLEND_COLOR,\n        // BLEND_COLOR_BURN,\n        // BLEND_COLOR_DODGE,\n        // BLEND_DARKEN,\n        // BLEND_DIFFERENCE,\n        // BLEND_DISSOLVE,\n        // BLEND_DIVIDE,\n        // BLEND_EXCLUSION,\n        // BLEND_HARD_LIGHT,\n        // BLEND_HUE,\n        // BLEND_LIGHTEN,\n        // BLEND_LINEAR_BURN,\n        // BLEND_LUMINOSITY,\n        // BLEND_MULTIPLY,\n        // BLEND_NORMAL,\n        // BLEND_OVERLAY,\n        // BLEND_SATURATION,\n        // BLEND_SCREEN,\n        // BLEND_SOFT_LIGHT,\n        // BLEND_SOURCE_OVER,\n        // BLEND_SUBTRACT,\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/custom/GPUImage1977Filter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.custom;\n\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageBrightnessFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageContrastFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageHueFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSaturationFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSepiaToneFilter;\n\npublic class GPUImage1977Filter extends GPUImageFilterGroup {\n    public GPUImage1977Filter() {\n        addFilter(new GPUImageSepiaToneFilter(0.35f));\n        addFilter(new GPUImageHueFilter(-30f));\n        addFilter(new GPUImageSaturationFilter(1.4f));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/custom/GPUImageAdenFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.custom;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\n\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageBrightnessFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageMultiplyBlendFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSaturationFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSepiaToneFilter;\n\npublic class GPUImageAdenFilter extends GPUImageFilterGroup {\n    public GPUImageAdenFilter() {\n        super();\n        addFilter(new GPUImageSepiaToneFilter(0.2f));\n        addFilter(new GPUImageBrightnessFilter(0.125f));\n        addFilter(new GPUImageSaturationFilter(1.4f));\n        final GPUImageMultiplyBlendFilter blendFilter = new GPUImageMultiplyBlendFilter();\n        final Bitmap bitmap = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888);\n        final Canvas canvas = new Canvas(bitmap);\n        canvas.drawColor(Color.argb((int) (0.1 * 255), 125, 105, 24));\n        blendFilter.setBitmap(bitmap);\n        addFilter(blendFilter);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/custom/GPUImageClarendonFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.custom;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\n\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageBrightnessFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageContrastFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageHueFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageOverlayBlendFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSaturationFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSepiaToneFilter;\n\npublic class GPUImageClarendonFilter extends GPUImageFilterGroup {\n    public GPUImageClarendonFilter() {\n        super();\n        addFilter(new GPUImageBrightnessFilter(0.15f));\n        addFilter(new GPUImageContrastFilter(1.25f));\n        addFilter(new GPUImageSaturationFilter(1.15f));\n        addFilter(new GPUImageSepiaToneFilter(0.15f));\n        addFilter(new GPUImageHueFilter(5));\n        final GPUImageOverlayBlendFilter blendFilter = new GPUImageOverlayBlendFilter();\n        final Bitmap bitmap = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888);\n        final Canvas canvas = new Canvas(bitmap);\n        canvas.drawColor(Color.argb((int) (0.4 * 255), 127, 187, 227));\n        blendFilter.setBitmap(bitmap);\n        addFilter(blendFilter);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/AdenFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.custom.GPUImageAdenFilter;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\n\npublic class AdenFilter extends Filter<GPUImageAdenFilter> {\n\n    private final GPUImageAdenFilter filter;\n\n    public AdenFilter() {\n        super(FiltersHelper.FilterType.ADEN, R.string.aden);\n        filter = new GPUImageAdenFilter();\n    }\n\n    @Override\n    public GPUImageAdenFilter getInstance() {\n        return filter;\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/BilateralBlurFilter.java",
    "content": "// package awais.instagrabber.fragments.imageedit.filters.filters;\n//\n// import java.util.Collections;\n// import java.util.Map;\n//\n// import awais.instagrabber.R;\n// import awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\n// import awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\n// import awais.instagrabber.fragments.imageedit.filters.properties.Property;\n// import jp.co.cyberagent.android.gpuimage.filter.GPUImageBilateralBlurFilter;\n//\n// public class BilateralBlurFilter extends Filter<GPUImageBilateralBlurFilter> {\n//     private static final int PROP_DISTANCE = 0;\n//\n//     private final GPUImageBilateralBlurFilter filter;\n//     private final Map<Integer, Property<?>> properties;\n//\n//     public BilateralBlurFilter() {\n//         super(FiltersHelper.FilterType.BILATERAL_BLUR, R.string.bilateral_blur);\n//         properties = Collections.singletonMap(\n//                 PROP_DISTANCE, new FloatProperty(-1, 8f, 0f, 15.0f)\n//         );\n//         filter = new GPUImageBilateralBlurFilter((Float) getProperty(PROP_DISTANCE).getDefaultValue());\n//     }\n//\n//     @Override\n//     public Map<Integer, Property<?>> getProperties() {\n//         return properties;\n//     }\n//\n//     @Override\n//     public void adjust(final int property, final Object value) {\n//         super.adjust(property, value);\n//         if (!(value instanceof Float)) return;\n//         filter.setDistanceNormalizationFactor((Float) value);\n//     }\n//\n//     @Override\n//     public GPUImageBilateralBlurFilter getInstance() {\n//         return filter;\n//     }\n// }\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/BoxBlurFilter.java",
    "content": "// package awais.instagrabber.fragments.imageedit.filters.filters;\n//\n// import java.util.Collections;\n// import java.util.Map;\n//\n// import awais.instagrabber.R;\n// import awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\n// import awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\n// import awais.instagrabber.fragments.imageedit.filters.properties.Property;\n// import jp.co.cyberagent.android.gpuimage.filter.GPUImageBoxBlurFilter;\n//\n// public class BoxBlurFilter extends Filter<GPUImageBoxBlurFilter> {\n//     private static final int PROP_SIZE = 0;\n//\n//     private final GPUImageBoxBlurFilter filter;\n//     private final Map<Integer, Property<?>> properties;\n//\n//     public BoxBlurFilter() {\n//         super(FiltersHelper.FilterType.BOX_BLUR, R.string.box_blur);\n//         properties = Collections.singletonMap(\n//                 PROP_SIZE, new FloatProperty(-1, 1f, 1f, 10.0f)\n//         );\n//         filter = new GPUImageBoxBlurFilter((Float) getProperty(PROP_SIZE).getDefaultValue());\n//     }\n//\n//     @Override\n//     public Map<Integer, Property<?>> getProperties() {\n//         return properties;\n//     }\n//\n//     @Override\n//     public void adjust(final int property, final Object value) {\n//         super.adjust(property, value);\n//         if (!(value instanceof Float)) return;\n//         filter.setBlurSize((Float) value);\n//     }\n//\n//     @Override\n//     public GPUImageBoxBlurFilter getInstance() {\n//         return filter;\n//     }\n// }\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/BrightnessFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageBrightnessFilter;\n\npublic class BrightnessFilter extends Filter<GPUImageBrightnessFilter> {\n    private static final int PROP_BRIGHTNESS = 0;\n\n    private final GPUImageBrightnessFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public BrightnessFilter() {\n        super(FiltersHelper.FilterType.BRIGHTNESS, R.string.brightness);\n        properties = Collections.singletonMap(\n                PROP_BRIGHTNESS, new FloatProperty(R.string.brightness, 0.0f, -1.0f, 1.0f)\n        );\n        filter = new GPUImageBrightnessFilter((Float) getProperty(PROP_BRIGHTNESS).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setBrightness((Float) value);\n    }\n\n    @Override\n    public GPUImageBrightnessFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/ClarendonFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.custom.GPUImageClarendonFilter;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\n\npublic class ClarendonFilter extends Filter<GPUImageClarendonFilter> {\n\n    private final GPUImageClarendonFilter filter;\n\n    public ClarendonFilter() {\n        super(FiltersHelper.FilterType.CLARENDON, R.string.clarendon);\n        filter = new GPUImageClarendonFilter();\n    }\n\n    @Override\n    public GPUImageClarendonFilter getInstance() {\n        return filter;\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/ContrastFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageContrastFilter;\n\npublic class ContrastFilter extends Filter<GPUImageContrastFilter> {\n    private static final int PROP_CONTRAST = 0;\n\n    private final GPUImageContrastFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public ContrastFilter() {\n        super(FiltersHelper.FilterType.CONTRAST, R.string.contrast);\n        properties = Collections.singletonMap(\n                PROP_CONTRAST, new FloatProperty(R.string.contrast, 1.0f, 0.0f, 4.0f)\n        );\n        filter = new GPUImageContrastFilter((Float) getProperty(PROP_CONTRAST).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setContrast((Float) value);\n    }\n\n    @Override\n    public GPUImageContrastFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/ExposureFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageExposureFilter;\n\npublic class ExposureFilter extends Filter<GPUImageExposureFilter> {\n    private static final int PROP_EXPOSURE = 0;\n\n    private final GPUImageExposureFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public ExposureFilter() {\n        super(FiltersHelper.FilterType.EXPOSURE, R.string.exposure);\n        properties = Collections.singletonMap(\n                PROP_EXPOSURE, new FloatProperty(R.string.exposure, 0f, -3.0f, 3.0f)\n        );\n        filter = new GPUImageExposureFilter((Float) getProperty(PROP_EXPOSURE).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setExposure((Float) value);\n    }\n\n    @Override\n    public GPUImageExposureFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/Filter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.StringRes;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\n\npublic abstract class Filter<T extends GPUImageFilter> {\n    private final FiltersHelper.FilterType type;\n    private final int label;\n\n    public Filter(final FiltersHelper.FilterType type, @StringRes final int label) {\n        this.type = type;\n        this.label = label;\n    }\n\n    public FiltersHelper.FilterType getType() {\n        return type;\n    }\n\n    @StringRes\n    public int getLabel() {\n        return label;\n    }\n\n    public abstract T getInstance();\n\n    public abstract Map<Integer, Property<?>> getProperties();\n\n    public Property<?> getProperty(int property) {\n        return getProperties().get(property);\n    }\n\n    @CallSuper\n    public void adjust(final int property, final Object value) {\n        final Property<?> propertyObj = getProperty(property);\n        propertyObj.setValue(value);\n    }\n\n    public void reset() {\n        final Map<Integer, Property<?>> propertyMap = getProperties();\n        if (propertyMap == null) return;\n        final Set<Map.Entry<Integer, Property<?>>> entries = propertyMap.entrySet();\n        for (final Map.Entry<Integer, Property<?>> entry : entries) {\n            adjust(entry.getKey(), entry.getValue().getDefaultValue());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/FilterFactory.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\n\npublic final class FilterFactory {\n\n    public static Filter<?> getInstance(final FiltersHelper.FilterType type) {\n        switch (type) {\n            case BRIGHTNESS:\n                return new BrightnessFilter();\n            case CONTRAST:\n                return new ContrastFilter();\n            case VIBRANCE:\n                return new VibranceFilter();\n            case SATURATION:\n                return new SaturationFilter();\n            case SHARPEN:\n                return new SharpenFilter();\n            case EXPOSURE:\n                return new ExposureFilter();\n            case NORMAL:\n                return new NormalFilter();\n            case SEPIA:\n                return new SepiaToneFilter();\n            case CLARENDON:\n                return new ClarendonFilter();\n            case ONE977:\n                return new One977Filter();\n            case ADEN:\n                return new AdenFilter();\n            // case BULGE_DISTORTION:\n            //     break;\n            // case CGA_COLORSPACE:\n            //     break;\n            // case COLOR_BALANCE:\n            //     break;\n            // case CROSSHATCH:\n            //     break;\n            // case DILATION:\n            //     break;\n            // case EMBOSS:\n            //     break;\n            // case FALSE_COLOR:\n            //     break;\n            // case GAMMA:\n            //     break;\n            // case GAUSSIAN_BLUR:\n            //     break;\n            // case GLASS_SPHERE:\n            //     break;\n            // case GRAYSCALE:\n            //     break;\n            // case HALFTONE:\n            //     break;\n            // case HAZE:\n            //     break;\n            // case HIGHLIGHT_SHADOW:\n            //     break;\n            // case HUE:\n            //     break;\n            // case INVERT:\n            //     break;\n            // case KUWAHARA:\n            //     break;\n            // case LAPLACIAN:\n            //     break;\n            // case LEVELS_FILTER_MIN:\n            //     break;\n            // case LOOKUP_AMATORKA:\n            //     break;\n            // case LUMINANCE:\n            //     break;\n            // case LUMINANCE_THRESHOLD:\n            //     break;\n            // case MONOCHROME:\n            //     break;\n            // case NON_MAXIMUM_SUPPRESSION:\n            //     break;\n            // case OPACITY:\n            //     break;\n            // case PIXELATION:\n            //     break;\n            // case POSTERIZE:\n            //     break;\n            // case RGB:\n            //     break;\n            // case RGB_DILATION:\n            //     break;\n            // case SKETCH:\n            //     break;\n            // case SMOOTH_TOON:\n            //     break;\n            // case SOBEL_EDGE_DETECTION:\n            //     break;\n            // case SOLARIZE:\n            //     break;\n            // case SPHERE_REFRACTION:\n            //     break;\n            // case SWIRL:\n            //     break;\n            // case THREE_X_THREE_CONVOLUTION:\n            //     break;\n            // case THRESHOLD_EDGE_DETECTION:\n            //     break;\n            // case TONE_CURVE:\n            //     break;\n            // case TOON:\n            //     break;\n            // case TRANSFORM2D:\n            //     break;\n            // case WEAK_PIXEL_INCLUSION:\n            //     break;\n            // case WHITE_BALANCE:\n            //     break;\n            // case ZOOM_BLUR:\n            //     break;\n            case VIGNETTE:\n                return new VignetteFilter();\n            // case BILATERAL_BLUR:\n            //     return new BilateralBlurFilter();\n            // case BOX_BLUR:\n            //     return new BoxBlurFilter();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/NormalFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\n\npublic class NormalFilter extends Filter<GPUImageFilter> {\n    private final GPUImageFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public NormalFilter() {\n        super(FiltersHelper.FilterType.NORMAL, R.string.normal);\n        properties = Collections.emptyMap();\n        filter = new GPUImageFilter();\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public GPUImageFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/One977Filter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.custom.GPUImage1977Filter;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\n\npublic class One977Filter extends Filter<GPUImage1977Filter> {\n\n    private final GPUImage1977Filter filter;\n\n    public One977Filter() {\n        super(FiltersHelper.FilterType.ONE977, R.string.one977);\n        filter = new GPUImage1977Filter();\n    }\n\n    @Override\n    public GPUImage1977Filter getInstance() {\n        return filter;\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/SaturationFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSaturationFilter;\n\npublic class SaturationFilter extends Filter<GPUImageSaturationFilter> {\n    private static final int PROP_SATURATION = 0;\n\n    private final GPUImageSaturationFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public SaturationFilter() {\n        super(FiltersHelper.FilterType.SATURATION, R.string.saturation);\n        properties = Collections.singletonMap(\n                PROP_SATURATION, new FloatProperty(R.string.saturation, 1.0f, 0f, 2.0f)\n        );\n        filter = new GPUImageSaturationFilter((Float) getProperty(PROP_SATURATION).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setSaturation((Float) value);\n    }\n\n    @Override\n    public GPUImageSaturationFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/SepiaToneFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSepiaToneFilter;\n\npublic class SepiaToneFilter extends Filter<GPUImageSepiaToneFilter> {\n    private static final int PROP_INTENSITY = 0;\n\n    private final GPUImageSepiaToneFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public SepiaToneFilter() {\n        super(FiltersHelper.FilterType.SEPIA, R.string.sepia);\n        properties = Collections.singletonMap(\n                PROP_INTENSITY, new FloatProperty(-1, 1f, 1f, 10.0f)\n        );\n        filter = new GPUImageSepiaToneFilter((Float) getProperty(PROP_INTENSITY).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setIntensity((Float) value);\n    }\n\n    @Override\n    public GPUImageSepiaToneFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/SharpenFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageSharpenFilter;\n\npublic class SharpenFilter extends Filter<GPUImageSharpenFilter> {\n    private static final int PROP_SHARPNESS = 0;\n\n    private final GPUImageSharpenFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public SharpenFilter() {\n        super(FiltersHelper.FilterType.SHARPEN, R.string.sharpen);\n        properties = Collections.singletonMap(\n                PROP_SHARPNESS, new FloatProperty(R.string.sharpen, 0f, -0.5f, 0.5f)\n        );\n        filter = new GPUImageSharpenFilter((Float) getProperty(PROP_SHARPNESS).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setSharpness((Float) value);\n    }\n\n    @Override\n    public GPUImageSharpenFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/VibranceFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageVibranceFilter;\n\npublic class VibranceFilter extends Filter<GPUImageVibranceFilter> {\n    private static final int PROP_VIBRANCE = 0;\n\n    private final GPUImageVibranceFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public VibranceFilter() {\n        super(FiltersHelper.FilterType.VIBRANCE, R.string.vibrance);\n        properties = Collections.singletonMap(\n                PROP_VIBRANCE, new FloatProperty(R.string.vibrance, 0f, -1.2f, 1.2f)\n        );\n        filter = new GPUImageVibranceFilter((Float) getProperty(PROP_VIBRANCE).getDefaultValue());\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        if (!(value instanceof Float)) return;\n        filter.setVibrance((Float) value);\n    }\n\n    @Override\n    public GPUImageVibranceFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/filters/VignetteFilter.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.filters;\n\nimport android.graphics.Color;\nimport android.graphics.PointF;\n\nimport com.google.common.collect.ImmutableMap;\n\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper;\nimport awais.instagrabber.fragments.imageedit.filters.properties.ColorProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.FloatProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.PointFProperty;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageVignetteFilter;\n\npublic class VignetteFilter extends Filter<GPUImageVignetteFilter> {\n    private static final int PROP_CENTER = 0;\n    private static final int PROP_COLOR = 1;\n    private static final int PROP_START = 2;\n    private static final int PROP_END = 3;\n\n    private final GPUImageVignetteFilter filter;\n    private final Map<Integer, Property<?>> properties;\n\n    public VignetteFilter() {\n        super(FiltersHelper.FilterType.VIGNETTE, R.string.vignette);\n        properties = ImmutableMap.of(\n                PROP_CENTER, new PointFProperty(R.string.center, new PointF(0.5f, 0.5f)),\n                PROP_COLOR, new ColorProperty(R.string.color, Color.BLACK),\n                PROP_START, new FloatProperty(R.string.start, 0.3f),\n                PROP_END, new FloatProperty(R.string.end, 0.75f)\n        );\n        filter = new GPUImageVignetteFilter(\n                (PointF) getProperty(PROP_CENTER).getDefaultValue(),\n                getFloatArrayFromColor((Integer) getProperty(PROP_COLOR).getDefaultValue()),\n                (Float) getProperty(PROP_START).getDefaultValue(),\n                (Float) getProperty(PROP_END).getDefaultValue()\n        );\n    }\n\n    @Override\n    public Map<Integer, Property<?>> getProperties() {\n        return properties;\n    }\n\n    @Override\n    public void adjust(final int property, final Object value) {\n        super.adjust(property, value);\n        switch (property) {\n            case PROP_CENTER:\n                filter.setVignetteCenter((PointF) value);\n                return;\n            case PROP_COLOR:\n                final int color = (int) value;\n                filter.setVignetteColor(getFloatArrayFromColor(color));\n                return;\n            case PROP_START:\n                filter.setVignetteStart((float) value);\n                return;\n            case PROP_END:\n                filter.setVignetteEnd((float) value);\n                return;\n            default:\n        }\n    }\n\n    private float[] getFloatArrayFromColor(final int color) {\n        return new float[]{Color.red(color) / 255f, Color.green(color) / 255f, Color.blue(color) / 255f};\n    }\n\n    @Override\n    public GPUImageVignetteFilter getInstance() {\n        return filter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/properties/ColorProperty.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.properties;\n\nimport androidx.annotation.StringRes;\n\n/**\n * Min and Max values do not matter here\n */\npublic class ColorProperty extends Property<Integer> {\n    private final int label;\n    private final int defaultValue;\n\n    public ColorProperty(@StringRes final int label,\n                         final int defaultValue) {\n        this.label = label;\n        this.defaultValue = defaultValue;\n    }\n\n    @Override\n    public int getLabel() {\n        return label;\n    }\n\n    @Override\n    public Integer getDefaultValue() {\n        return defaultValue;\n    }\n\n    @Override\n    public Integer getMinValue() {\n        return defaultValue;\n    }\n\n    @Override\n    public Integer getMaxValue() {\n        return defaultValue;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/properties/FloatProperty.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.properties;\n\nimport androidx.annotation.StringRes;\n\npublic class FloatProperty extends Property<Float> {\n\n    private final int label;\n    private final float defaultValue;\n    private final float minValue;\n    private final float maxValue;\n\n    public FloatProperty(@StringRes final int label,\n                         final float defaultValue,\n                         final float minValue,\n                         final float maxValue) {\n\n        this.label = label;\n        this.defaultValue = defaultValue;\n        this.minValue = minValue;\n        this.maxValue = maxValue;\n    }\n\n    public FloatProperty(@StringRes final int label, final float value) {\n        this.label = label;\n        this.defaultValue = value;\n        this.minValue = value;\n        this.maxValue = value;\n    }\n\n    @Override\n    public int getLabel() {\n        return label;\n    }\n\n    @Override\n    public Float getDefaultValue() {\n        return defaultValue;\n    }\n\n    @Override\n    public Float getMinValue() {\n        return minValue;\n    }\n\n    @Override\n    public Float getMaxValue() {\n        return maxValue;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/properties/PointFProperty.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.properties;\n\nimport android.graphics.PointF;\n\nimport androidx.annotation.StringRes;\n\n/**\n * Min and Max values do not matter here\n */\npublic class PointFProperty extends Property<PointF> {\n    private final int label;\n    private final PointF defaultValue;\n\n    public PointFProperty(@StringRes final int label,\n                          final PointF defaultValue) {\n        this.label = label;\n        this.defaultValue = defaultValue;\n    }\n\n    @Override\n    public int getLabel() {\n        return label;\n    }\n\n    @Override\n    public PointF getDefaultValue() {\n        return defaultValue;\n    }\n\n    @Override\n    public PointF getMinValue() {\n        return defaultValue;\n    }\n\n    @Override\n    public PointF getMaxValue() {\n        return defaultValue;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/imageedit/filters/properties/Property.java",
    "content": "package awais.instagrabber.fragments.imageedit.filters.properties;\n\nimport android.util.Log;\n\nimport androidx.annotation.StringRes;\n\npublic abstract class Property<T> {\n    private static final String TAG = Property.class.getSimpleName();\n    protected T value;\n\n    @StringRes\n    public abstract int getLabel();\n\n    public abstract T getDefaultValue();\n\n    public abstract T getMinValue();\n\n    public abstract T getMaxValue();\n\n    public T getValue() {\n        return value;\n    }\n\n    public void setValue(final Object value) {\n        try {\n            //noinspection unchecked\n            this.value = (T) value;\n        } catch (ClassCastException e) {\n            Log.e(TAG, \"setValue: \", e);\n        }\n    }\n\n    public void reset() {\n        setValue(getDefaultValue());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java",
    "content": "package awais.instagrabber.fragments.main;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.Set;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.asyncs.DiscoverPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentDiscoverBinding;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.PostItemType;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.DiscoverService;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"DiscoverFragment\";\n\n    private MainActivity fragmentActivity;\n    private SwipeRefreshLayout root;\n    private FragmentDiscoverBinding binding;\n    private ActionMode actionMode;\n    private boolean isLoggedIn, shouldRefresh = true;\n    private String keyword;\n    private Set<Media> selectedFeedModels;\n    private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_TOPIC_POSTS_LAYOUT);\n\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.posts.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.multi_select_download_menu,\n            new PrimaryActionModeCallback.CallbacksHelper() {\n                @Override\n                public void onDestroy(final ActionMode mode) {\n                    binding.posts.endSelection();\n                }\n\n                @Override\n                public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n                    if (item.getItemId() == R.id.action_download) {\n                        if (selectedFeedModels == null) return false;\n                        final Context context = getContext();\n                        if (context == null) return false;\n                        DownloadUtils.download(context, ImmutableList.copyOf(DiscoverFragment.this.selectedFeedModels));\n                        binding.posts.endSelection();\n                    }\n                    return false;\n                }\n            });\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            try {\n                final NavDirections commentsAction = ProfileFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getPk(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(DiscoverFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = ProfileFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(DiscoverFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media feedModel) {\n            final Location location = feedModel.getLocation();\n            if (location == null) return;\n            try {\n                final NavDirections action = ProfileFragmentDirections.actionToLocation(location.getPk());\n                NavHostFragment.findNavController(DiscoverFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            navigateToProfile(\"@\" + feedModel.getUser().getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            final User user = feedModel.getUser();\n            if (user == null) return;\n            navigateToProfile(\"@\" + user.getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(final Media feedModel, final int position) {\n            try {\n                final NavDirections action = DiscoverFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(DiscoverFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n        }\n    };\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            DiscoverFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) getActivity();\n        setHasOptionsMenu(true);\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentDiscoverBinding.inflate(getLayoutInflater(), container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.swipeRefreshLayout.setOnRefreshListener(this);\n        init();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.saved_viewer_menu, menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.posts.refresh();\n    }\n\n    private void init() {\n        final Bundle arguments = getArguments();\n        if (arguments == null) return;\n        final DiscoverFragmentArgs fragmentArgs = DiscoverFragmentArgs.fromBundle(arguments);\n        keyword = fragmentArgs.getKeyword();\n        setupPosts();\n    }\n\n    private void setupPosts() {\n        binding.posts.setViewModelStoreOwner(this)\n                .setLifeCycleOwner(this)\n                .setPostFetchService(new DiscoverPostFetchService(new DiscoverService.TopicalExploreRequest()))\n                .setLayoutPreferences(layoutPreferences)\n                .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                .setFeedItemCallback(feedItemCallback)\n                .setSelectionModeCallback(selectionModeCallback)\n                .init();\n        binding.swipeRefreshLayout.setRefreshing(true);\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() ->\n                binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())\n        );\n    }\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = DiscoverFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                Constants.PREF_TOPIC_POSTS_LAYOUT,\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);\n                });\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java",
    "content": "package awais.instagrabber.fragments.main;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.ActionMode;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.activity.OnBackPressedCallback;\nimport androidx.activity.OnBackPressedDispatcher;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.fragment.app.Fragment;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.FeedAdapterV2;\nimport awais.instagrabber.adapters.FeedStoriesAdapter;\nimport awais.instagrabber.asyncs.FeedPostFetchService;\nimport awais.instagrabber.customviews.PrimaryActionModeCallback;\nimport awais.instagrabber.databinding.FragmentFeedBinding;\nimport awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.repositories.requests.StoryViewerOptions;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.stories.Story;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.viewmodels.FeedStoriesViewModel;\nimport awais.instagrabber.webservices.StoriesRepository;\nimport kotlinx.coroutines.Dispatchers;\n\npublic class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {\n    private static final String TAG = \"FeedFragment\";\n\n    private MainActivity fragmentActivity;\n    private CoordinatorLayout root;\n    private FragmentFeedBinding binding;\n    private StoriesRepository storiesRepository;\n    private boolean shouldRefresh = true;\n    private FeedStoriesViewModel feedStoriesViewModel;\n    private boolean storiesFetching;\n    private ActionMode actionMode;\n    private Set<Media> selectedFeedModels;\n    private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_POSTS_LAYOUT);\n    private MenuItem storyListMenu;\n\n    private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(\n            new FeedStoriesAdapter.OnFeedStoryClickListener() {\n                @Override\n                public void onFeedStoryClick(Story model, int position) {\n                    final NavController navController = NavHostFragment.findNavController(FeedFragment.this);\n                    if (isSafeToNavigate(navController)) {\n                        try {\n                            final NavDirections action = FeedFragmentDirections.actionToStory(StoryViewerOptions.forFeedStoryPosition(position));\n                            navController.navigate(action);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"onFeedStoryClick: \", e);\n                        }\n                    }\n                }\n\n                @Override\n                public void onFeedStoryLongClick(Story model, int position) {\n                    final User user = model.getUser();\n                    if (user == null) return;\n                    navigateToProfile(\"@\" + user.getUsername());\n                }\n            }\n    );\n\n    private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {\n        @Override\n        public void onPostClick(final Media feedModel) {\n            openPostDialog(feedModel, -1);\n        }\n\n        @Override\n        public void onSliderClick(final Media feedModel, final int position) {\n            openPostDialog(feedModel, position);\n        }\n\n        @Override\n        public void onCommentsClick(final Media feedModel) {\n            try {\n                final User user = feedModel.getUser();\n                if (user == null) return;\n                final NavDirections commentsAction = FeedFragmentDirections.actionToComments(\n                        feedModel.getCode(),\n                        feedModel.getPk(),\n                        user.getPk()\n                );\n                NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);\n            } catch (Exception e) {\n                Log.e(TAG, \"onCommentsClick: \", e);\n            }\n        }\n\n        @Override\n        public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) {\n            final Context context = getContext();\n            if (context == null) return;\n            DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation);\n        }\n\n        @Override\n        public void onHashtagClick(final String hashtag) {\n            try {\n                final NavDirections action = FeedFragmentDirections.actionToHashtag(hashtag);\n                NavHostFragment.findNavController(FeedFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onHashtagClick: \", e);\n            }\n        }\n\n        @Override\n        public void onLocationClick(final Media feedModel) {\n            final Location location = feedModel.getLocation();\n            if (location == null) return;\n            try {\n                final NavDirections action = FeedFragmentDirections.actionToLocation(location.getPk());\n                NavHostFragment.findNavController(FeedFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onLocationClick: \", e);\n            }\n        }\n\n        @Override\n        public void onMentionClick(final String mention) {\n            navigateToProfile(mention.trim());\n        }\n\n        @Override\n        public void onNameClick(final Media feedModel) {\n            if (feedModel.getUser() == null) return;\n            navigateToProfile(\"@\" + feedModel.getUser().getUsername());\n        }\n\n        @Override\n        public void onProfilePicClick(final Media feedModel) {\n            if (feedModel.getUser() == null) return;\n            navigateToProfile(\"@\" + feedModel.getUser().getUsername());\n        }\n\n        @Override\n        public void onURLClick(final String url) {\n            Utils.openURL(getContext(), url);\n        }\n\n        @Override\n        public void onEmailClick(final String emailId) {\n            Utils.openEmailAddress(getContext(), emailId);\n        }\n\n        private void openPostDialog(final Media feedModel, final int position) {\n            try {\n                final NavDirections action = FeedFragmentDirections.actionToPost(feedModel, position);\n                NavHostFragment.findNavController(FeedFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"openPostDialog: \", e);\n            }\n        }\n    };\n    private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {\n        @Override\n        public void handleOnBackPressed() {\n            binding.feedRecyclerView.endSelection();\n        }\n    };\n    private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(\n            R.menu.multi_select_download_menu,\n            new PrimaryActionModeCallback.CallbacksHelper() {\n                @Override\n                public void onDestroy(final ActionMode mode) {\n                    binding.feedRecyclerView.endSelection();\n                }\n\n                @Override\n                public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {\n                    if (item.getItemId() == R.id.action_download) {\n                        if (FeedFragment.this.selectedFeedModels == null) return false;\n                        final Context context = getContext();\n                        if (context == null) return false;\n                        DownloadUtils.download(context, ImmutableList.copyOf(FeedFragment.this.selectedFeedModels));\n                        binding.feedRecyclerView.endSelection();\n                        return true;\n                    }\n                    return false;\n                }\n            });\n    private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {\n\n        @Override\n        public void onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled()) {\n                final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();\n                onBackPressedCallback.setEnabled(true);\n                onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);\n            }\n            if (actionMode == null) {\n                actionMode = fragmentActivity.startActionMode(multiSelectAction);\n            }\n        }\n\n        @Override\n        public void onSelectionChange(final Set<Media> selectedFeedModels) {\n            final String title = getString(R.string.number_selected, selectedFeedModels.size());\n            if (actionMode != null) {\n                actionMode.setTitle(title);\n            }\n            FeedFragment.this.selectedFeedModels = selectedFeedModels;\n        }\n\n        @Override\n        public void onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled()) {\n                onBackPressedCallback.setEnabled(false);\n                onBackPressedCallback.remove();\n            }\n            if (actionMode != null) {\n                actionMode.finish();\n                actionMode = null;\n            }\n        }\n    };\n\n    private void navigateToProfile(final String username) {\n        try {\n            final NavDirections action = FeedFragmentDirections.actionToProfile().setUsername(username);\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"navigateToProfile: \", e);\n        }\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fragmentActivity = (MainActivity) requireActivity();\n        storiesRepository = StoriesRepository.Companion.getInstance();\n        setHasOptionsMenu(true);\n    }\n\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater,\n                             final ViewGroup container,\n                             final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentFeedBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        binding.feedSwipeRefreshLayout.setOnRefreshListener(this);\n        /*\n        FabAnimation.init(binding.fabCamera);\n        FabAnimation.init(binding.fabStory);\n        binding.fabAdd.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                isRotate = FabAnimation.rotateFab(v, !isRotate);\n                if (isRotate) {\n                    FabAnimation.showIn(binding.fabCamera);\n                    FabAnimation.showIn(binding.fabStory);\n                }\n                else {\n                    FabAnimation.showOut(binding.fabCamera);\n                    FabAnimation.showOut(binding.fabStory);\n                }\n            }\n        });\n         */\n        setupFeedStories();\n        setupFeed();\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {\n        inflater.inflate(R.menu.feed_menu, menu);\n        storyListMenu = menu.findItem(R.id.storyList);\n        storyListMenu.setVisible(!storiesFetching);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(@NonNull final MenuItem item) {\n        if (item.getItemId() == R.id.storyList) {\n            try {\n                final NavDirections action = FeedFragmentDirections.actionToStoryList(\"feed\");\n                NavHostFragment.findNavController(FeedFragment.this).navigate(action);\n            } catch (Exception e) {\n                Log.e(TAG, \"onOptionsItemSelected: \", e);\n            }\n        } else if (item.getItemId() == R.id.layout) {\n            showPostsLayoutPreferences();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onRefresh() {\n        binding.feedRecyclerView.refresh();\n        fetchStories();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        fragmentActivity.setToolbar(binding.toolbar, this);\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        fragmentActivity.resetToolbar(this);\n    }\n\n    private void setupFeed() {\n        binding.feedRecyclerView.setViewModelStoreOwner(this)\n                                .setLifeCycleOwner(this)\n                                .setPostFetchService(new FeedPostFetchService())\n                                .setLayoutPreferences(layoutPreferences)\n                                .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())\n                                .setFeedItemCallback(feedItemCallback)\n                                .setSelectionModeCallback(selectionModeCallback)\n                                .init();\n        // binding.feedRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {\n        //     @Override\n        //     public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {\n        //         super.onScrolled(recyclerView, dx, dy);\n        //         final boolean canScrollVertically = recyclerView.canScrollVertically(-1);\n        //         final MotionScene.Transition transition = root.getTransition(R.id.transition);\n        //         if (transition != null) {\n        //             transition.setEnable(!canScrollVertically);\n        //         }\n        //     }\n        // });\n        // if (shouldAutoPlay) {\n        //     videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();\n        //     binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);\n        // }\n    }\n\n    private void updateSwipeRefreshState() {\n        AppExecutors.INSTANCE.getMainThread().execute(() -> binding.feedSwipeRefreshLayout\n                .setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching)\n        );\n    }\n\n    private void setupFeedStories() {\n        if (storyListMenu != null) storyListMenu.setVisible(false);\n        feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);\n        final Context context = getContext();\n        if (context == null) return;\n        final RecyclerView storiesRecyclerView = binding.header;\n        storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));\n        storiesRecyclerView.setAdapter(feedStoriesAdapter);\n        feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList);\n        fetchStories();\n    }\n\n    private void fetchStories() {\n        if (storiesFetching) return;\n        // final String cookie = settingsHelper.getString(Constants.COOKIE);\n        storiesFetching = true;\n        updateSwipeRefreshState();\n        storiesRepository.getFeedStories(\n                CoroutineUtilsKt.getContinuation((feedStoryModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"failed\", throwable);\n                        storiesFetching = false;\n                        updateSwipeRefreshState();\n                        return;\n                    }\n                    storiesFetching = false;\n                    //noinspection unchecked\n                    if (Utils.settingsHelper.getBoolean(PreferenceKeys.HIDE_MUTED_REELS)) {\n                        feedStoriesViewModel.getList().postValue(feedStoryModels\n                                .stream()\n                                .filter(s -> s.getMuted() != true)\n                                .collect(Collectors.toList()));\n                    }\n                    feedStoriesViewModel.getList().postValue((List<Story>) feedStoryModels);\n                    if (storyListMenu != null) storyListMenu.setVisible(true);\n                    updateSwipeRefreshState();\n                }), Dispatchers.getIO())\n        );\n    }\n\n    private void showPostsLayoutPreferences() {\n        final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(\n                Constants.PREF_POSTS_LAYOUT,\n                preferences -> {\n                    layoutPreferences = preferences;\n                    new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200);\n                }\n        );\n        fragment.show(getChildFragmentManager(), \"posts_layout_preferences\");\n    }\n\n    public void scrollToTop() {\n        if (binding != null) {\n            binding.feedRecyclerView.smoothScrollToPosition(0);\n            // binding.storiesContainer.setExpanded(true);\n        }\n    }\n\n    private boolean isSafeToNavigate(final NavController navController) {\n        return navController.getCurrentDestination() != null\n                && navController.getCurrentDestination().getId() == R.id.feedFragment;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt",
    "content": "package awais.instagrabber.fragments.main\n\nimport android.content.Intent\nimport android.graphics.Typeface\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.text.SpannableStringBuilder\nimport android.text.style.RelativeSizeSpan\nimport android.text.style.StyleSpan\nimport android.util.Log\nimport android.view.*\nimport android.widget.Toast\nimport androidx.activity.OnBackPressedCallback\nimport androidx.appcompat.content.res.AppCompatResources\nimport androidx.appcompat.widget.TooltipCompat\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentTransaction\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.navigation.fragment.findNavController\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener\nimport awais.instagrabber.R\nimport awais.instagrabber.activities.MainActivity\nimport awais.instagrabber.adapters.FeedAdapterV2\nimport awais.instagrabber.adapters.HighlightsAdapter\nimport awais.instagrabber.asyncs.ProfilePostFetchService\nimport awais.instagrabber.customviews.PrimaryActionModeCallback\nimport awais.instagrabber.customviews.RamboTextViewV2\nimport awais.instagrabber.customviews.RamboTextViewV2.*\nimport awais.instagrabber.databinding.FragmentProfileBinding\nimport awais.instagrabber.db.repositories.FavoriteRepository\nimport awais.instagrabber.dialogs.*\nimport awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback\nimport awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback\nimport awais.instagrabber.dialogs.MultiOptionDialogFragment.Option\nimport awais.instagrabber.fragments.UserSearchMode\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.enums.PostItemType\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.FriendshipStatus\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.UserProfileContextLink\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.utils.extensions.isReallyPrivate\nimport awais.instagrabber.utils.extensions.trimAll\nimport awais.instagrabber.viewmodels.AppStateViewModel\nimport awais.instagrabber.viewmodels.ProfileFragmentViewModel\nimport awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileEvent.*\nimport awais.instagrabber.viewmodels.ProfileFragmentViewModelFactory\nimport awais.instagrabber.webservices.*\n\n\nclass ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCallback, MultiOptionDialogSingleCallback<String> {\n    private var backStackSavedStateResultLiveData: MutableLiveData<Any?>? = null\n    private var shareDmMenuItem: MenuItem? = null\n    private var shareLinkMenuItem: MenuItem? = null\n    private var removeFollowerMenuItem: MenuItem? = null\n    private var chainingMenuItem: MenuItem? = null\n    private var mutePostsMenuItem: MenuItem? = null\n    private var muteStoriesMenuItem: MenuItem? = null\n    private var restrictMenuItem: MenuItem? = null\n    private var blockMenuItem: MenuItem? = null\n    private var setupPostsDone: Boolean = false\n    private var selectedMedia: List<Media>? = null\n    private var actionMode: ActionMode? = null\n    private var disableDm: Boolean = false\n\n    // private var shouldRefresh: Boolean = true\n    private var highlightsAdapter: HighlightsAdapter? = null\n    private var layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_PROFILE_POSTS_LAYOUT)\n\n    private lateinit var mainActivity: MainActivity\n\n    // private lateinit var root: MotionLayout\n    private lateinit var binding: FragmentProfileBinding\n    private lateinit var appStateViewModel: AppStateViewModel\n    private lateinit var viewModel: ProfileFragmentViewModel\n\n    private val userRepository by lazy { UserRepository.getInstance() }\n    private val friendshipRepository by lazy { FriendshipRepository.getInstance() }\n    private val storiesRepository by lazy { StoriesRepository.getInstance() }\n    private val mediaRepository by lazy { MediaRepository.getInstance() }\n    private val graphQLRepository by lazy { GraphQLRepository.getInstance() }\n    private val favoriteRepository by lazy { FavoriteRepository.getInstance(requireContext()) }\n    private val directMessagesRepository by lazy { DirectMessagesRepository.getInstance() }\n\n    private val confirmDialogFragmentRequestCode = 100\n    private val ppOptsDialogRequestCode = 101\n    private val bioDialogRequestCode = 102\n    private val translationDialogRequestCode = 103\n    private val feedItemCallback: FeedAdapterV2.FeedItemCallback = object : FeedAdapterV2.FeedItemCallback {\n        override fun onPostClick(media: Media) {\n            openPostDialog(media, -1)\n        }\n\n        override fun onProfilePicClick(media: Media) {\n            navigateToProfile(media.user?.username)\n        }\n\n        override fun onNameClick(media: Media) {\n            navigateToProfile(media.user?.username)\n        }\n\n        override fun onLocationClick(media: Media?) {\n            try {\n                val action = ProfileFragmentDirections.actionToLocation(media?.location?.pk ?: return)\n                findNavController().navigate(action)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onLocationClick: \", e)\n            }\n        }\n\n        override fun onMentionClick(mention: String?) {\n            navigateToProfile(mention?.trimAll() ?: return)\n        }\n\n        override fun onHashtagClick(hashtag: String?) {\n            try {\n                val action = ProfileFragmentDirections.actionToHashtag(hashtag ?: return)\n                findNavController().navigate(action)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onHashtagClick: \", e)\n            }\n        }\n\n        override fun onCommentsClick(media: Media?) {\n            try {\n                val commentsAction = ProfileFragmentDirections.actionToComments(\n                    media?.code ?: return,\n                    media.pk ?: return,\n                    media.user?.pk ?: return\n                )\n                findNavController().navigate(commentsAction)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onCommentsClick: \", e)\n            }\n        }\n\n        override fun onDownloadClick(media: Media?, childPosition: Int, popupLocation: View) {\n            DownloadUtils.showDownloadDialog(context ?: return, media ?: return, childPosition, popupLocation)\n        }\n\n        override fun onEmailClick(emailId: String?) {\n            Utils.openEmailAddress(context ?: return, emailId ?: return)\n        }\n\n        override fun onURLClick(url: String?) {\n            Utils.openURL(context ?: return, url ?: return)\n        }\n\n        override fun onSliderClick(media: Media?, position: Int) {\n            openPostDialog(media ?: return, position)\n        }\n    }\n    private val onBackPressedCallback = object : OnBackPressedCallback(false) {\n        override fun handleOnBackPressed() {\n            binding.postsRecyclerView.endSelection()\n        }\n    }\n    private val multiSelectAction = PrimaryActionModeCallback(\n        R.menu.multi_select_download_menu,\n        object : PrimaryActionModeCallback.CallbacksHelper() {\n            override fun onDestroy(mode: ActionMode?) {\n                binding.postsRecyclerView.endSelection()\n            }\n\n            override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {\n                val item1 = item ?: return false\n                if (item1.itemId == R.id.action_download) {\n                    val selectedMedia = this@ProfileFragment.selectedMedia ?: return false\n                    val context = context ?: return false\n                    DownloadUtils.download(context, selectedMedia)\n                    binding.postsRecyclerView.endSelection()\n                    return true\n                }\n                return false\n            }\n        }\n    )\n    private val selectionModeCallback = object : FeedAdapterV2.SelectionModeCallback {\n        override fun onSelectionStart() {\n            if (!onBackPressedCallback.isEnabled) {\n                onBackPressedCallback.isEnabled = true\n                mainActivity.onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)\n            }\n            if (actionMode == null) {\n                actionMode = mainActivity.startActionMode(multiSelectAction)\n            }\n        }\n\n        override fun onSelectionChange(mediaSet: Set<Media>?) {\n            if (mediaSet == null) {\n                selectedMedia = null\n                return\n            }\n            val title = getString(R.string.number_selected, mediaSet.size)\n            actionMode?.title = title\n            selectedMedia = mediaSet.toList()\n        }\n\n        override fun onSelectionEnd() {\n            if (onBackPressedCallback.isEnabled) {\n                onBackPressedCallback.isEnabled = false\n                onBackPressedCallback.remove()\n            }\n            (actionMode ?: return).finish()\n            actionMode = null\n        }\n    }\n    private val onProfilePicClickListener = View.OnClickListener {\n        val hasStories = viewModel.userStories.value?.data != null\n        if (!hasStories) {\n            showProfilePicDialog()\n            return@OnClickListener\n        }\n        val dialog = MultiOptionDialogFragment.newInstance(\n            ppOptsDialogRequestCode,\n            0,\n            arrayListOf(\n                Option(getString(R.string.view_pfp), \"profile_pic\"),\n                Option(getString(R.string.show_stories), \"show_stories\")\n            )\n        )\n        dialog.show(childFragmentManager, MultiOptionDialogFragment::class.java.simpleName)\n    }\n    private val onFollowersClickListener = View.OnClickListener {\n        try {\n            val action = ProfileFragmentDirections.actionToFollowViewer(\n                viewModel.profile.value?.data?.pk ?: return@OnClickListener,\n                true,\n                viewModel.profile.value?.data?.username ?: return@OnClickListener\n            )\n            findNavController().navigate(action)\n        } catch (e: Exception) {\n            Log.e(TAG, \"onFollowersClickListener: \", e)\n        }\n    }\n    private val onFollowingClickListener = View.OnClickListener {\n        try {\n            val action = ProfileFragmentDirections.actionToFollowViewer(\n                viewModel.profile.value?.data?.pk ?: return@OnClickListener,\n                false,\n                viewModel.profile.value?.data?.username ?: return@OnClickListener\n            )\n            findNavController().navigate(action)\n        } catch (e: Exception) {\n            Log.e(TAG, \"onFollowersClickListener: \", e)\n        }\n    }\n    private val onEmailClickListener = OnEmailClickListener {\n        Utils.openEmailAddress(context ?: return@OnEmailClickListener, it.originalText.trimAll())\n    }\n    private val onHashtagClickListener = OnHashtagClickListener {\n        try {\n            val actionToHashtag = ProfileFragmentDirections.actionToHashtag(it.originalText.trimAll())\n            findNavController().navigate(actionToHashtag)\n        } catch (e: Exception) {\n            Log.e(TAG, \"onHashtagClickListener: \", e)\n        }\n    }\n    private val onMentionClickListener = OnMentionClickListener {\n        navigateToProfile(it.originalText.trimAll())\n    }\n    private val onURLClickListener = OnURLClickListener {\n        Utils.openURL(context ?: return@OnURLClickListener, it.originalText.trimAll())\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    private val backStackSavedStateObserver = Observer<Any?> { result ->\n        if (result == null) return@Observer\n        if ((result is RankedRecipient)) {\n            if (context != null) {\n                Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show()\n            }\n            viewModel.shareDm(result)\n        } else if ((result is Set<*>)) {\n            try {\n                if (context != null) {\n                    Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show()\n                }\n                viewModel.shareDm(result as Set<RankedRecipient>)\n            } catch (e: Exception) {\n                Log.e(TAG, \"share: \", e)\n            }\n        }\n        // clear result\n        backStackSavedStateResultLiveData?.postValue(null)\n    }\n\n    private fun openPostDialog(media: Media, position: Int) {\n        try {\n            val actionToPost = ProfileFragmentDirections.actionToPost(media, position)\n            findNavController().navigate(actionToPost)\n        } catch (e: Exception) {\n            Log.e(TAG, \"openPostDialog: \", e)\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        mainActivity = requireActivity() as MainActivity\n        appStateViewModel = ViewModelProvider(mainActivity).get(AppStateViewModel::class.java)\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        val csrfToken = getCsrfTokenFromCookie(cookie)\n        val userId = getUserIdFromCookie(cookie)\n        val isLoggedIn = !csrfToken.isNullOrBlank() && userId != 0L && deviceUuid.isNotBlank()\n        viewModel = ViewModelProvider(\n            this,\n            ProfileFragmentViewModelFactory(\n                csrfToken,\n                deviceUuid,\n                userRepository,\n                friendshipRepository,\n                storiesRepository,\n                mediaRepository,\n                graphQLRepository,\n                favoriteRepository,\n                directMessagesRepository,\n                if (isLoggedIn) DirectMessagesManager else null,\n                this,\n                arguments\n            )\n        ).get(ProfileFragmentViewModel::class.java)\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {\n        binding = FragmentProfileBinding.inflate(inflater, container, false)\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        init()\n    }\n\n    override fun onRefresh() {\n        viewModel.refresh()\n        binding.postsRecyclerView.refresh()\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        inflater.inflate(R.menu.profile_menu, menu)\n        blockMenuItem = menu.findItem(R.id.block)\n        restrictMenuItem = menu.findItem(R.id.restrict)\n        muteStoriesMenuItem = menu.findItem(R.id.mute_stories)\n        mutePostsMenuItem = menu.findItem(R.id.mute_posts)\n        chainingMenuItem = menu.findItem(R.id.chaining)\n        removeFollowerMenuItem = menu.findItem(R.id.remove_follower)\n        shareLinkMenuItem = menu.findItem(R.id.share_link)\n        shareDmMenuItem = menu.findItem(R.id.share_dm)\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        when (item.itemId) {\n            R.id.layout -> showPostsLayoutPreferences()\n            R.id.restrict -> viewModel.restrictUser()\n            R.id.block -> viewModel.blockUser()\n            R.id.chaining -> navigateToChaining()\n            R.id.mute_stories -> viewModel.muteStories()\n            R.id.mute_posts -> viewModel.mutePosts()\n            R.id.remove_follower -> viewModel.removeFollower()\n            R.id.share_link -> shareProfileLink()\n            R.id.share_dm -> shareProfileViaDm()\n        }\n        return true\n    }\n\n    override fun onResume() {\n        super.onResume()\n        mainActivity.setToolbar(binding.toolbar, this)\n        try {\n            val backStackEntry = NavHostFragment.findNavController(this).currentBackStackEntry\n            if (backStackEntry != null) {\n                backStackSavedStateResultLiveData = backStackEntry.savedStateHandle.getLiveData(\"result\")\n                backStackSavedStateResultLiveData?.observe(viewLifecycleOwner, backStackSavedStateObserver)\n            }\n            mainActivity.supportActionBar?.title = viewModel.username.value\n            val (currentUserData, profileData) = viewModel.currentUserProfileActionLiveData.value!!\n            setupOptionsMenuItems(currentUserData.data, profileData.data)\n        } catch (e: Exception) {\n            Log.e(TAG, \"onResume: \", e)\n        }\n    }\n\n    override fun onStop() {\n        super.onStop()\n        mainActivity.resetToolbar(this)\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        setupPostsDone = false\n    }\n\n    private fun shareProfileViaDm() {\n        try {\n            val actionToUserSearch = ProfileFragmentDirections.actionToUserSearch().apply {\n                title = getString(R.string.share)\n                actionLabel = getString(R.string.send)\n                showGroups = true\n                multiple = true\n                searchMode = UserSearchMode.RAVEN\n            }\n            findNavController().navigate(actionToUserSearch)\n        } catch (e: Exception) {\n            Log.e(TAG, \"shareProfileViaDm: \", e)\n        }\n    }\n\n    private fun shareProfileLink() {\n        val profile = viewModel.profile.value?.data ?: return\n        val sharingIntent = Intent(Intent.ACTION_SEND)\n        sharingIntent.type = \"text/plain\"\n        sharingIntent.putExtra(Intent.EXTRA_TEXT, \"https://instagram.com/\" + profile.username)\n        startActivity(Intent.createChooser(sharingIntent, null))\n    }\n\n    private fun navigateToChaining() {\n        viewModel.currentUser.value?.data ?: return\n        val profile = viewModel.profile.value?.data ?: return\n        try {\n            val actionToNotifications = ProfileFragmentDirections.actionToNotifications().apply {\n                type = \"chaining\"\n                targetId = profile.pk\n            }\n            findNavController().navigate(actionToNotifications)\n        } catch (e: Exception) {\n            Log.e(TAG, \"navigateToChaining: \", e)\n        }\n    }\n\n    private fun init() {\n        binding.swipeRefreshLayout.setOnRefreshListener(this)\n        disableDm = !isNavRootInCurrentTabs(\"direct_messages_nav_graph\")\n        setupHighlights()\n        setupObservers()\n    }\n\n    private fun setupObservers() {\n        appStateViewModel.currentUserLiveData.observe(viewLifecycleOwner, viewModel::setCurrentUser)\n        viewModel.isLoggedIn.observe(viewLifecycleOwner) {\n            // observe so that `isLoggedIn.value` is correct\n            Log.d(TAG, \"setupObservers: $it\")\n        }\n        viewModel.currentUserProfileActionLiveData.observe(viewLifecycleOwner) {\n            val (currentUserResource, profileResource) = it\n            if (currentUserResource.status == Resource.Status.ERROR || profileResource.status == Resource.Status.ERROR) {\n                context?.let { ctx -> Toast.makeText(ctx, R.string.error_loading_profile, Toast.LENGTH_LONG).show() }\n                return@observe\n            }\n            if (currentUserResource.status == Resource.Status.LOADING || profileResource.status == Resource.Status.LOADING) {\n                binding.swipeRefreshLayout.isRefreshing = true\n                return@observe\n            }\n            binding.swipeRefreshLayout.isRefreshing = false\n            val currentUser = currentUserResource.data\n            val profile = profileResource.data\n            val stateUsername = arguments?.getString(\"username\")\n            setupOptionsMenuItems(currentUser, profile)\n            if (currentUser == null && profile == null && stateUsername.isNullOrBlank()) {\n                // default anonymous state, show default message\n                showDefaultMessage()\n                return@observe\n            }\n            if (profile == null && !stateUsername.isNullOrBlank()) {\n                context?.let { ctx -> Toast.makeText(ctx, R.string.error_loading_profile, Toast.LENGTH_LONG).show() }\n                return@observe\n            }\n            setupFavChip(profile, currentUser)\n            setupFavButton(currentUser, profile)\n            setupSavedButton(currentUser, profile)\n            setupTaggedButton(profile)\n            setupLikedButton(currentUser, profile)\n            setupDMButton(currentUser, profile)\n            if (profile == null) return@observe\n            if (profile.isReallyPrivate(currentUser)) {\n                showPrivateAccountMessage()\n                return@observe\n            }\n            if (!setupPostsDone) {\n                setupPosts(profile, currentUser)\n            }\n        }\n        viewModel.username.observe(viewLifecycleOwner) {\n            mainActivity.supportActionBar?.title = it\n            mainActivity.supportActionBar?.subtitle = null\n        }\n        viewModel.profilePicUrl.observe(viewLifecycleOwner) {\n            val visibility = if (it.isNullOrBlank()) View.INVISIBLE else View.VISIBLE\n            binding.header.mainProfileImage.visibility = visibility\n            binding.header.mainProfileImage.setImageURI(if (it.isNullOrBlank()) null else it)\n            binding.header.mainProfileImage.setOnClickListener(if (it.isNullOrBlank()) null else onProfilePicClickListener)\n        }\n        viewModel.fullName.observe(viewLifecycleOwner) { binding.header.mainFullName.text = it ?: \"\" }\n        viewModel.biography.observe(viewLifecycleOwner, this::setupBiography)\n        viewModel.url.observe(viewLifecycleOwner, this::setupProfileURL)\n        viewModel.followersCount.observe(viewLifecycleOwner, this::setupFollowers)\n        viewModel.followingCount.observe(viewLifecycleOwner, this::setupFollowing)\n        viewModel.postCount.observe(viewLifecycleOwner, this::setupPostsCount)\n        viewModel.friendshipStatus.observe(viewLifecycleOwner) {\n            setupFollowButton(it)\n            setupMainStatus(it)\n        }\n        viewModel.isVerified.observe(viewLifecycleOwner) {\n            binding.header.isVerified.visibility = if (it == true) View.VISIBLE else View.GONE\n        }\n        viewModel.isPrivate.observe(viewLifecycleOwner) {\n            binding.header.isPrivate.visibility = if (it == true) View.VISIBLE else View.GONE\n        }\n        viewModel.isFavorite.observe(viewLifecycleOwner) {\n            if (!it) {\n                binding.header.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24)\n                binding.header.favChip.setText(R.string.add_to_favorites)\n                return@observe\n            }\n            binding.header.favChip.setChipIconResource(R.drawable.ic_star_check_24)\n            binding.header.favChip.setText(R.string.favorite_short)\n        }\n        viewModel.profileContext.observe(viewLifecycleOwner, this::setupProfileContext)\n        viewModel.userHighlights.observe(viewLifecycleOwner) {\n            binding.header.highlightsList.visibility = if (it.data.isNullOrEmpty()) View.GONE else View.VISIBLE\n            highlightsAdapter?.submitList(it.data)\n        }\n        viewModel.userStories.observe(viewLifecycleOwner) {\n            binding.header.mainProfileImage.setStoriesBorder(if (it.data == null) 0 else 1)\n        }\n        viewModel.eventLiveData.observe(viewLifecycleOwner) {\n            val event = it?.getContentIfNotHandled() ?: return@observe\n            when (event) {\n                ShowConfirmUnfollowDialog -> showConfirmUnfollowDialog()\n                is DMButtonState -> binding.header.btnDM.isEnabled = !event.disabled\n                is NavigateToThread -> mainActivity.navigateToThread(event.threadId, event.username)\n                is ShowTranslation -> showTranslationDialog(event.result)\n            }\n        }\n    }\n\n    private fun showPrivateAccountMessage() {\n        binding.header.mainFollowers.isClickable = false\n        binding.header.mainFollowing.isClickable = false\n        binding.privatePage.visibility = VISIBLE\n        binding.privatePage.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT\n        binding.privatePage1.setImageResource(R.drawable.lock)\n        binding.privatePage2.setText(R.string.priv_acc)\n        binding.privatePage.visibility = VISIBLE\n        binding.privatePage1.visibility = VISIBLE\n        binding.privatePage2.visibility = VISIBLE\n        binding.postsRecyclerView.visibility = GONE\n        binding.swipeRefreshLayout.isRefreshing = false\n    }\n\n    private fun setupProfileContext(contextPair: Pair<String?, List<UserProfileContextLink>?>) {\n        val (profileContext, contextLinkList) = contextPair\n        if (profileContext == null || contextLinkList == null) {\n            binding.header.profileContext.visibility = GONE\n            binding.header.profileContext.clearOnMentionClickListeners()\n            return\n        }\n        var updatedProfileContext: String = profileContext\n        contextLinkList.forEachIndexed { i, link ->\n            if (link.username == null) return@forEachIndexed\n            updatedProfileContext = updatedProfileContext.substring(0, link.start + i) + \"@\" + updatedProfileContext.substring(link.start + i)\n        }\n        binding.header.profileContext.visibility = VISIBLE\n        binding.header.profileContext.text = updatedProfileContext\n        binding.header.profileContext.addOnMentionClickListener(onMentionClickListener)\n    }\n\n    private fun setupProfileURL(url: String?) {\n        if (url.isNullOrBlank()) {\n            binding.header.mainUrl.visibility = GONE\n            binding.header.mainUrl.clearOnURLClickListeners()\n            binding.header.mainUrl.setOnLongClickListener(null)\n            return\n        }\n        binding.header.mainUrl.visibility = VISIBLE\n        binding.header.mainUrl.text = url\n        binding.header.mainUrl.addOnURLClickListener { Utils.openURL(context ?: return@addOnURLClickListener, it.originalText.trimAll()) }\n        binding.header.mainUrl.setOnLongClickListener {\n            Utils.copyText(context ?: return@setOnLongClickListener false, url.trimAll())\n            return@setOnLongClickListener true\n        }\n    }\n\n    private fun showTranslationDialog(result: String) {\n        val dialog = ConfirmDialogFragment.newInstance(\n            translationDialogRequestCode,\n            0,\n            result,\n            R.string.ok,\n            0,\n            0\n        )\n        dialog.show(childFragmentManager, ConfirmDialogFragment::class.java.simpleName)\n    }\n\n    private fun setupBiography(bio: String?) {\n        if (bio.isNullOrBlank()) {\n            binding.header.mainBiography.visibility = View.GONE\n            binding.header.mainBiography.clearAllAutoLinkListeners()\n            binding.header.mainBiography.setOnLongClickListener(null)\n            return\n        }\n        binding.header.mainBiography.visibility = View.VISIBLE\n        binding.header.mainBiography.text = bio\n        setCommonAutoLinkListeners(binding.header.mainBiography)\n        binding.header.mainBiography.setOnLongClickListener {\n            val isLoggedIn = viewModel.isLoggedIn.value ?: false\n            val options = arrayListOf(Option(getString(R.string.bio_copy), \"copy\"))\n            if (isLoggedIn) {\n                options.add(Option(getString(R.string.bio_translate), \"translate\"))\n            }\n            val dialog = MultiOptionDialogFragment.newInstance(\n                bioDialogRequestCode,\n                0,\n                options\n            )\n            dialog.show(childFragmentManager, MultiOptionDialogFragment::class.java.simpleName)\n            return@setOnLongClickListener true\n        }\n    }\n\n    private fun setCommonAutoLinkListeners(textView: RamboTextViewV2) {\n        textView.addOnEmailClickListener(onEmailClickListener)\n        textView.addOnHashtagListener(onHashtagClickListener)\n        textView.addOnMentionClickListener(onMentionClickListener)\n        textView.addOnURLClickListener(onURLClickListener)\n    }\n\n    private fun setupOptionsMenuItems(currentUser: User?, profile: User?) {\n        val isMe = currentUser?.pk == profile?.pk\n        if (profile == null || (currentUser != null && isMe)) {\n            hideAllOptionsMenuItems()\n            return\n        }\n        if (currentUser == null) {\n            hideAllOptionsMenuItems()\n            shareLinkMenuItem?.isVisible = profile.username.isNotBlank()\n            return\n        }\n\n        blockMenuItem?.isVisible = true\n        blockMenuItem?.setTitle(if (profile.friendshipStatus?.blocking == true) R.string.unblock else R.string.block)\n\n        restrictMenuItem?.isVisible = true\n        restrictMenuItem?.setTitle(if (profile.friendshipStatus?.isRestricted == true) R.string.unrestrict else R.string.restrict)\n\n        muteStoriesMenuItem?.isVisible = true\n        muteStoriesMenuItem?.setTitle(if (profile.friendshipStatus?.isMutingReel == true) R.string.unmute_stories else R.string.mute_stories)\n\n        mutePostsMenuItem?.isVisible = true\n        mutePostsMenuItem?.setTitle(if (profile.friendshipStatus?.muting == true) R.string.unmute_posts else R.string.mute_posts)\n\n        chainingMenuItem?.isVisible = profile.hasChaining\n        removeFollowerMenuItem?.isVisible = profile.friendshipStatus?.followedBy ?: false\n        shareLinkMenuItem?.isVisible = profile.username.isNotBlank()\n        shareDmMenuItem?.isVisible = profile.pk != 0L\n    }\n\n    private fun hideAllOptionsMenuItems() {\n        blockMenuItem?.isVisible = false\n        restrictMenuItem?.isVisible = false\n        muteStoriesMenuItem?.isVisible = false\n        mutePostsMenuItem?.isVisible = false\n        chainingMenuItem?.isVisible = false\n        removeFollowerMenuItem?.isVisible = false\n        shareLinkMenuItem?.isVisible = false\n        shareDmMenuItem?.isVisible = false\n    }\n\n    private fun setupPostsCount(count: Long?) {\n        if (count == null) {\n            binding.header.mainPostCount.visibility = View.GONE\n            return\n        }\n        binding.header.mainPostCount.visibility = View.VISIBLE\n        binding.header.mainPostCount.text = getCountSpan(R.plurals.main_posts_count, abbreviate(count, null), count)\n        if (count >= 1000) {\n            TooltipCompat.setTooltipText(binding.header.mainPostCount, count.toString(10))\n        }\n    }\n\n    private fun setupFollowing(count: Long?) {\n        if (count == null) {\n            binding.header.mainFollowing.visibility = View.GONE\n            return\n        }\n        val abbreviate = abbreviate(count, null)\n        val span = SpannableStringBuilder(getString(R.string.main_posts_following, abbreviate))\n        binding.header.mainFollowing.visibility = View.VISIBLE\n        binding.header.mainFollowing.text = getCountSpan(span, abbreviate)\n        if (count <= 0) {\n            binding.header.mainFollowing.setOnClickListener(null)\n            return\n        }\n        binding.header.mainFollowing.setOnClickListener(onFollowingClickListener)\n        if (count >= 1000) {\n            TooltipCompat.setTooltipText(binding.header.mainFollowing, count.toString(10))\n        }\n    }\n\n    private fun setupFollowers(count: Long?) {\n        if (count == null) {\n            binding.header.mainFollowers.visibility = View.GONE\n            return\n        }\n        binding.header.mainFollowers.visibility = View.VISIBLE\n        binding.header.mainFollowers.text = getCountSpan(R.plurals.main_posts_followers, abbreviate(count, null), count)\n        if (count <= 0) {\n            binding.header.mainFollowers.setOnClickListener(null)\n            return\n        }\n        binding.header.mainFollowers.setOnClickListener(onFollowersClickListener)\n        if (count >= 1000) {\n            TooltipCompat.setTooltipText(binding.header.mainFollowers, count.toString(10))\n        }\n    }\n\n    private fun setupDMButton(currentUser: User?, profile: User?) {\n        val visibility = if (disableDm || (currentUser != null && profile?.pk == currentUser.pk)) View.GONE else View.VISIBLE\n        binding.header.btnDM.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.btnDM.setOnClickListener(null)\n            return\n        }\n        binding.header.btnDM.setOnClickListener { viewModel.sendDm() }\n    }\n\n    private fun setupLikedButton(currentUser: User?, profile: User?) {\n        val visibility = if (currentUser != null && profile?.pk == currentUser.pk) View.VISIBLE else View.GONE\n        binding.header.btnLiked.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.btnLiked.setOnClickListener(null)\n            return\n        }\n        binding.header.btnLiked.setOnClickListener {\n            try {\n                val action = ProfileFragmentDirections.actionToSaved(\n                    viewModel.profile.value?.data?.username ?: return@setOnClickListener,\n                    viewModel.profile.value?.data?.pk ?: return@setOnClickListener,\n                    PostItemType.LIKED\n                )\n                findNavController().navigate(action)\n            } catch (e: Exception) {\n                Log.e(TAG, \"setupLikedButton: \", e)\n            }\n        }\n    }\n\n    private fun setupTaggedButton(profile: User?) {\n        val visibility = if (profile?.usertagsCount == 0L) View.GONE else View.VISIBLE\n        binding.header.btnTagged.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.btnTagged.setOnClickListener(null)\n            return\n        }\n        binding.header.btnTagged.setOnClickListener {\n            try {\n                val action = ProfileFragmentDirections.actionToSaved(\n                    viewModel.profile.value?.data?.username ?: return@setOnClickListener,\n                    viewModel.profile.value?.data?.pk ?: return@setOnClickListener,\n                    PostItemType.TAGGED\n                )\n                findNavController().navigate(action)\n            } catch (e: Exception) {\n                Log.e(TAG, \"setupTaggedButton: \", e)\n            }\n        }\n    }\n\n    private fun setupSavedButton(currentUser: User?, profile: User?) {\n        val visibility = if (currentUser != null && profile?.pk == currentUser.pk) View.VISIBLE else View.GONE\n        binding.header.btnSaved.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.btnSaved.setOnClickListener(null)\n            return\n        }\n        binding.header.btnSaved.setOnClickListener {\n            try {\n                val action = ProfileFragmentDirections.actionToSavedCollections().apply { isSaving = false }\n                findNavController().navigate(action)\n            } catch (e: Exception) {\n                Log.e(TAG, \"setupSavedButton: \", e)\n            }\n        }\n    }\n\n    private fun setupFavButton(currentUser: User?, profile: User?) {\n        val visibility = if (currentUser != null && profile?.pk != currentUser.pk) View.VISIBLE else View.GONE\n        binding.header.btnFollow.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.btnFollow.setOnClickListener(null)\n            return\n        }\n        binding.header.btnFollow.setOnClickListener { viewModel.toggleFollow(false) }\n    }\n\n    private fun setupFavChip(profile: User?, currentUser: User?) {\n        val visibility = if (profile?.pk != currentUser?.pk) View.VISIBLE else View.GONE\n        binding.header.favChip.visibility = visibility\n        if (visibility == View.GONE) {\n            binding.header.favChip.setOnClickListener(null)\n            return\n        }\n        binding.header.favChip.setOnClickListener { viewModel.toggleFavorite() }\n    }\n\n    private fun setupFollowButton(it: FriendshipStatus?) {\n        if (it == null) return\n        if (it.following) {\n            binding.header.btnFollow.setText(R.string.unfollow)\n            binding.header.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24)\n            return\n        }\n        if (it.outgoingRequest) {\n            binding.header.btnFollow.setText(R.string.cancel)\n            binding.header.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24)\n            return\n        }\n        binding.header.btnFollow.setText(R.string.follow)\n        binding.header.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_24)\n    }\n\n    private fun setupMainStatus(it: FriendshipStatus?) {\n        if (it == null || (!it.following && !it.followedBy)) {\n            binding.header.mainStatus.visibility = View.GONE\n            return\n        }\n        binding.header.mainStatus.visibility = View.VISIBLE\n        if (it.following && it.followedBy) {\n            context?.let { ctx ->\n                binding.header.mainStatus.chipBackgroundColor = AppCompatResources.getColorStateList(ctx, R.color.green_800)\n                binding.header.mainStatus.setText(R.string.status_mutual)\n            }\n            return\n        }\n        if (it.following) {\n            context?.let { ctx ->\n                binding.header.mainStatus.chipBackgroundColor = AppCompatResources.getColorStateList(ctx, R.color.deep_orange_800)\n                binding.header.mainStatus.setText(R.string.status_following)\n            }\n            return\n        }\n        context?.let { ctx ->\n            binding.header.mainStatus.chipBackgroundColor = AppCompatResources.getColorStateList(ctx, R.color.blue_800)\n            binding.header.mainStatus.setText(R.string.status_follower)\n        }\n    }\n\n    private fun getCountSpan(pluralRes: Int, countString: String, count: Long): SpannableStringBuilder {\n        val span = SpannableStringBuilder(resources.getQuantityString(pluralRes, count.toInt(), countString))\n        return getCountSpan(span, countString)\n    }\n\n    private fun getCountSpan(span: SpannableStringBuilder, countString: String): SpannableStringBuilder {\n        span.setSpan(RelativeSizeSpan(1.2f), 0, countString.length, 0)\n        span.setSpan(StyleSpan(Typeface.BOLD), 0, countString.length, 0)\n        return span\n    }\n\n    private fun showDefaultMessage() {\n        binding.header.root.visibility = GONE\n        binding.swipeRefreshLayout.visibility = GONE\n        binding.privatePage.visibility = VISIBLE\n        binding.privatePage1.visibility = VISIBLE\n        binding.privatePage2.visibility = VISIBLE\n        binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24)\n        binding.privatePage2.setText(R.string.no_acc)\n    }\n\n    private fun setupHighlights() {\n        val context = context ?: return\n        highlightsAdapter = HighlightsAdapter { model, position ->\n            val options = StoryViewerOptions.forHighlight(model.user!!.pk, \"\").apply { currentFeedStoryIndex = position }\n            val action = ProfileFragmentDirections.actionToStory(options)\n            NavHostFragment.findNavController(this).navigate(action)\n        }\n        binding.header.highlightsList.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)\n        binding.header.highlightsList.adapter = highlightsAdapter\n    }\n\n    private fun setupPosts(profile: User, currentUser: User?) {\n        binding.postsRecyclerView.setViewModelStoreOwner(this)\n            .setLifeCycleOwner(this)\n            .setPostFetchService(ProfilePostFetchService(profile, currentUser != null))\n            .setLayoutPreferences(layoutPreferences)\n            .addFetchStatusChangeListener {\n                AppExecutors.mainThread.execute {\n                    binding.swipeRefreshLayout.isRefreshing = it\n                }\n            }\n            .setFeedItemCallback(feedItemCallback)\n            .setSelectionModeCallback(selectionModeCallback)\n            .init()\n        // binding.postsRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {\n        //     override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {\n        //         super.onScrolled(recyclerView, dx, dy)\n        //         val canScrollVertically = recyclerView.canScrollVertically(-1)\n        //         if (!canScrollVertically) {\n        //             (binding.collapsingToolbarLayout.layoutParams as AppBarLayout.LayoutParams).scrollFlags = 0\n        //         }\n        //     }\n        // })\n        setupPostsDone = true\n    }\n\n    private fun navigateToProfile(username: String?) {\n        try {\n            val username1 = username ?: return\n            val actionToProfile = ProfileFragmentDirections.actionToProfile().apply { this.username = username1 }\n            findNavController().navigate(actionToProfile)\n        } catch (e: Exception) {\n            Log.e(TAG, \"navigateToProfile: \", e)\n        }\n    }\n\n    private fun showConfirmUnfollowDialog() {\n        val isPrivate = viewModel.profile.value?.data?.isPrivate ?: return\n        val titleRes = if (isPrivate) R.string.priv_acc else 0\n        val messageRes = if (isPrivate) R.string.priv_acc_confirm else R.string.are_you_sure\n        val dialog = ConfirmDialogFragment.newInstance(\n            confirmDialogFragmentRequestCode,\n            titleRes,\n            messageRes,\n            R.string.confirm,\n            R.string.cancel,\n            0,\n        )\n        dialog.show(childFragmentManager, ConfirmDialogFragment::class.java.simpleName)\n    }\n\n    override fun onPositiveButtonClicked(requestCode: Int) {\n        when (requestCode) {\n            confirmDialogFragmentRequestCode -> {\n                viewModel.toggleFollow(true)\n            }\n        }\n    }\n\n    override fun onNegativeButtonClicked(requestCode: Int) {}\n\n    override fun onNeutralButtonClicked(requestCode: Int) {}\n\n    override fun onSelect(requestCode: Int, result: String?) {\n        val r = result ?: return\n        when (requestCode) {\n            ppOptsDialogRequestCode -> onPpOptionSelect(r)\n            bioDialogRequestCode -> onBioOptionSelect(r)\n        }\n    }\n\n    private fun onBioOptionSelect(result: String) {\n        when (result) {\n            \"copy\" -> Utils.copyText(context ?: return, viewModel.biography.value ?: return)\n            \"translate\" -> viewModel.translateBio()\n        }\n    }\n\n    private fun onPpOptionSelect(result: String) {\n        when (result) {\n            \"profile_pic\" -> showProfilePicDialog()\n            \"show_stories\" -> {\n                try {\n                    val action = ProfileFragmentDirections.actionToStory(\n                        StoryViewerOptions.forUser(\n                            viewModel.profile.value?.data?.pk ?: return,\n                            viewModel.profile.value?.data?.username ?: return,\n                        )\n                    )\n                    findNavController().navigate(action)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"omPpOptionSelect: \", e)\n                }\n            }\n        }\n    }\n\n    override fun onCancel(requestCode: Int) {}\n\n    private fun showProfilePicDialog() {\n        val profile = viewModel.profile.value?.data ?: return\n        val fragment = ProfilePicDialogFragment.getInstance(\n            profile.pk,\n            profile.username,\n            profile.profilePicUrl ?: return\n        )\n        val ft = childFragmentManager.beginTransaction()\n        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)\n            .add(fragment, ProfilePicDialogFragment::class.java.simpleName)\n            .commit()\n    }\n\n    private fun showPostsLayoutPreferences() {\n        val fragment = PostsLayoutPreferencesDialogFragment(Constants.PREF_PROFILE_POSTS_LAYOUT) {\n            layoutPreferences = it\n            Handler(Looper.getMainLooper()).postDelayed(\n                { binding.postsRecyclerView.layoutPreferences = it },\n                200\n            )\n        }\n        fragment.show(childFragmentManager, PostsLayoutPreferencesDialogFragment::class.java.simpleName)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/search/SearchCategoryFragment.java",
    "content": "package awais.instagrabber.fragments.search;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.adapters.SearchItemsAdapter;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.search.SearchItem;\nimport awais.instagrabber.viewmodels.SearchFragmentViewModel;\n\npublic class SearchCategoryFragment extends Fragment {\n    private static final String TAG = SearchCategoryFragment.class.getSimpleName();\n    private static final String ARG_TYPE = \"type\";\n\n\n    @Nullable\n    private SwipeRefreshLayout swipeRefreshLayout;\n    @Nullable\n    private RecyclerView list;\n    private SearchFragmentViewModel viewModel;\n    private FavoriteType type;\n    private SearchItemsAdapter searchItemsAdapter;\n    @Nullable\n    private OnSearchItemClickListener onSearchItemClickListener;\n    private boolean skipViewRefresh;\n    private String prevQuery;\n\n    @NonNull\n    public static SearchCategoryFragment newInstance(@NonNull final FavoriteType type) {\n        final SearchCategoryFragment fragment = new SearchCategoryFragment();\n        final Bundle args = new Bundle();\n        args.putSerializable(ARG_TYPE, type);\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public SearchCategoryFragment() {}\n\n    @Override\n    public void onAttach(@NonNull final Context context) {\n        super.onAttach(context);\n        final Fragment parentFragment = getParentFragment();\n        if (!(parentFragment instanceof OnSearchItemClickListener)) return;\n        onSearchItemClickListener = (OnSearchItemClickListener) parentFragment;\n    }\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final FragmentActivity fragmentActivity = getActivity();\n        if (fragmentActivity == null) return;\n        viewModel = new ViewModelProvider(fragmentActivity).get(SearchFragmentViewModel.class);\n        final Bundle args = getArguments();\n        if (args == null) {\n            Log.e(TAG, \"onCreate: arguments are null\");\n            return;\n        }\n        final Serializable typeSerializable = args.getSerializable(ARG_TYPE);\n        if (!(typeSerializable instanceof FavoriteType)) {\n            Log.e(TAG, \"onCreate: type not a FavoriteType\");\n            return;\n        }\n        type = (FavoriteType) typeSerializable;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        final Context context = getContext();\n        if (context == null) return null;\n        skipViewRefresh = false;\n        if (swipeRefreshLayout != null) {\n            skipViewRefresh = true;\n            return swipeRefreshLayout;\n        }\n        swipeRefreshLayout = new SwipeRefreshLayout(context);\n        swipeRefreshLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        list = new RecyclerView(context);\n        list.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        swipeRefreshLayout.addView(list);\n        return swipeRefreshLayout;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (skipViewRefresh) return;\n        setupList();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        // Log.d(TAG, \"onResume: type: \" + type);\n        setupObservers();\n        final String currentQuery = viewModel.getQuery().getValue();\n        if (prevQuery != null && currentQuery != null && !Objects.equals(prevQuery, currentQuery)) {\n            viewModel.search(currentQuery, type);\n        }\n        prevQuery = null;\n    }\n\n    private void setupList() {\n        if (list == null || swipeRefreshLayout == null) return;\n        final Context context = getContext();\n        if (context == null) return;\n        list.setLayoutManager(new LinearLayoutManager(context));\n        searchItemsAdapter = new SearchItemsAdapter(onSearchItemClickListener);\n        list.setAdapter(searchItemsAdapter);\n        swipeRefreshLayout.setOnRefreshListener(() -> {\n            String currentQuery = viewModel.getQuery().getValue();\n            if (currentQuery == null) currentQuery = \"\";\n            viewModel.search(currentQuery, type);\n        });\n    }\n\n    private void setupObservers() {\n        viewModel.getQuery().observe(getViewLifecycleOwner(), q -> {\n            if (!isVisible() || Objects.equals(prevQuery, q)) return;\n            viewModel.search(q, type);\n            prevQuery = q;\n        });\n        final LiveData<Resource<List<SearchItem>>> resultsLiveData = getResultsLiveData();\n        if (resultsLiveData != null) {\n            resultsLiveData.observe(getViewLifecycleOwner(), this::onResults);\n        }\n    }\n\n    private void onResults(final Resource<List<SearchItem>> listResource) {\n        if (listResource == null) return;\n        switch (listResource.status) {\n            case SUCCESS:\n                if (searchItemsAdapter != null) {\n                    searchItemsAdapter.submitList(listResource.data);\n                }\n                if (swipeRefreshLayout != null) {\n                    swipeRefreshLayout.setRefreshing(false);\n                }\n                break;\n            case ERROR:\n                if (searchItemsAdapter != null) {\n                    searchItemsAdapter.submitList(Collections.emptyList());\n                }\n                if (swipeRefreshLayout != null) {\n                    swipeRefreshLayout.setRefreshing(false);\n                }\n                break;\n            case LOADING:\n                if (swipeRefreshLayout != null) {\n                    swipeRefreshLayout.setRefreshing(true);\n                }\n                break;\n            default:\n                break;\n        }\n    }\n\n    @Nullable\n    private LiveData<Resource<List<SearchItem>>> getResultsLiveData() {\n        switch (type) {\n            case TOP:\n                return viewModel.getTopResults();\n            case USER:\n                return viewModel.getUserResults();\n            case HASHTAG:\n                return viewModel.getHashtagResults();\n            case LOCATION:\n                return viewModel.getLocationResults();\n        }\n        return null;\n    }\n\n    public interface OnSearchItemClickListener {\n        void onSearchItemClick(SearchItem searchItem);\n\n        void onSearchItemDelete(SearchItem searchItem, FavoriteType type);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java",
    "content": "package awais.instagrabber.fragments.search;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.LinearLayoutCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.Observer;\nimport androidx.lifecycle.ViewModelProvider;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\n\nimport com.google.android.material.snackbar.Snackbar;\nimport com.google.android.material.tabs.TabLayoutMediator;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.adapters.SearchCategoryAdapter;\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter;\nimport awais.instagrabber.databinding.FragmentSearchBinding;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.search.SearchItem;\nimport awais.instagrabber.viewmodels.SearchFragmentViewModel;\n\nimport static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_SEARCH_FOCUS_KEYBOARD;\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class SearchFragment extends Fragment implements SearchCategoryFragment.OnSearchItemClickListener {\n    private static final String TAG = SearchFragment.class.getSimpleName();\n    private static final String QUERY = \"query\";\n\n    private FragmentSearchBinding binding;\n    private LinearLayoutCompat root;\n    private boolean shouldRefresh = true;\n    @Nullable\n    private EditText searchInput;\n    @Nullable\n    private MainActivity mainActivity;\n    private SearchFragmentViewModel viewModel;\n\n    private final TextWatcherAdapter textWatcher = new TextWatcherAdapter() {\n        @Override\n        public void afterTextChanged(final Editable s) {\n            if (s == null) return;\n            viewModel.submitQuery(s.toString().trim());\n        }\n    };\n\n    @Override\n    public void onCreate(@Nullable final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        final FragmentActivity fragmentActivity = getActivity();\n        if (!(fragmentActivity instanceof MainActivity)) return;\n        mainActivity = (MainActivity) fragmentActivity;\n        viewModel = new ViewModelProvider(mainActivity).get(SearchFragmentViewModel.class);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {\n        if (root != null) {\n            shouldRefresh = false;\n            return root;\n        }\n        binding = FragmentSearchBinding.inflate(inflater, container, false);\n        root = binding.getRoot();\n        return root;\n    }\n\n    @Override\n    public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {\n        if (!shouldRefresh) return;\n        init(savedInstanceState);\n        shouldRefresh = false;\n    }\n\n    @Override\n    public void onSaveInstanceState(@NonNull final Bundle outState) {\n        super.onSaveInstanceState(outState);\n        final String current = viewModel.getQuery().getValue();\n        if (TextUtils.isEmpty(current)) return;\n        outState.putString(QUERY, current);\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        if (mainActivity != null) {\n            mainActivity.hideSearchView();\n        }\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mainActivity != null) {\n            mainActivity.hideSearchView();\n        }\n        if (searchInput != null) {\n            searchInput.removeTextChangedListener(textWatcher);\n            searchInput.setText(\"\");\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        if (mainActivity != null) {\n            mainActivity.showSearchView();\n        }\n        if (settingsHelper.getBoolean(PREF_SEARCH_FOCUS_KEYBOARD)) {\n            if (searchInput != null) {\n                searchInput.requestFocus();\n            }\n            final InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE);\n            if (imm != null) imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);\n        }\n    }\n\n    private void init(@Nullable final Bundle savedInstanceState) {\n        if (mainActivity == null) return;\n        searchInput = mainActivity.showSearchView().getEditText();\n        setupObservers();\n        setupViewPager();\n        setupSearchInput(savedInstanceState);\n    }\n\n    private void setupObservers() {\n        viewModel.getQuery().observe(getViewLifecycleOwner(), q -> {}); // need to observe, so that getQuery returns proper value\n    }\n\n    private void setupSearchInput(@Nullable final Bundle savedInstanceState) {\n        if (searchInput == null) return;\n        searchInput.removeTextChangedListener(textWatcher); // make sure we add only 1 instance of textWatcher\n        searchInput.addTextChangedListener(textWatcher);\n        boolean triggerEmptyQuery = true;\n        if (savedInstanceState != null) {\n            final String savedQuery = savedInstanceState.getString(QUERY);\n            if (TextUtils.isEmpty(savedQuery)) return;\n            searchInput.setText(savedQuery);\n            triggerEmptyQuery = false;\n        }\n        if (settingsHelper.getBoolean(PREF_SEARCH_FOCUS_KEYBOARD)) {\n            searchInput.requestFocus();\n            final InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE);\n            if (imm != null) imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);\n        }\n        if (triggerEmptyQuery) {\n            viewModel.submitQuery(\"\");\n        }\n    }\n\n    private void setupViewPager() {\n        binding.pager.setSaveEnabled(false);\n        final List<FavoriteType> categories = Arrays.asList(FavoriteType.values());\n        binding.pager.setAdapter(new SearchCategoryAdapter(this, categories));\n        final TabLayoutMediator mediator = new TabLayoutMediator(binding.tabLayout, binding.pager, (tab, position) -> {\n            try {\n                final FavoriteType type = categories.get(position);\n                final int resId;\n                switch (type) {\n                    case TOP:\n                        resId = R.string.top;\n                        break;\n                    case USER:\n                        resId = R.string.accounts;\n                        break;\n                    case HASHTAG:\n                        resId = R.string.hashtags;\n                        break;\n                    case LOCATION:\n                        resId = R.string.locations;\n                        break;\n                    default:\n                        throw new IllegalStateException(\"Unexpected value: \" + type);\n                }\n                tab.setText(resId);\n            } catch (Exception e) {\n                Log.e(TAG, \"setupViewPager: \", e);\n            }\n        });\n        mediator.attach();\n    }\n\n    @Override\n    public void onSearchItemClick(final SearchItem searchItem) {\n        if (searchItem == null) return;\n        final FavoriteType type = searchItem.getType();\n        if (type == null) return;\n        try {\n            if (!searchItem.isFavorite()) {\n                viewModel.saveToRecentSearches(searchItem); // insert or update recent\n            }\n            final NavDirections action;\n            switch (type) {\n                case USER:\n                    action = SearchFragmentDirections.actionToProfile().setUsername(searchItem.getUser().getUsername());\n                    break;\n                case HASHTAG:\n                    action = SearchFragmentDirections.actionToHashtag(searchItem.getHashtag().getName());\n                    break;\n                case LOCATION:\n                    action = SearchFragmentDirections.actionToLocation(searchItem.getPlace().getLocation().getPk());\n                    break;\n                default:\n                    return;\n            }\n            NavHostFragment.findNavController(this).navigate(action);\n        } catch (Exception e) {\n            Log.e(TAG, \"onSearchItemClick: \", e);\n        }\n    }\n\n    @Override\n    public void onSearchItemDelete(final SearchItem searchItem, final FavoriteType type) {\n        final LiveData<Resource<Object>> liveData = viewModel.deleteRecentSearch(searchItem);\n        if (liveData == null) return;\n        liveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {\n            @Override\n            public void onChanged(final Resource<Object> resource) {\n                if (resource == null) return;\n                switch (resource.status) {\n                    case SUCCESS:\n                        viewModel.search(\"\", type);\n                        viewModel.search(\"\", FavoriteType.TOP);\n                        liveData.removeObserver(this);\n                        break;\n                    case ERROR:\n                        Snackbar.make(binding.getRoot(), R.string.error, Snackbar.LENGTH_SHORT).show();\n                        liveData.removeObserver(this);\n                        break;\n                    case LOADING:\n                    default:\n                        break;\n                }\n            }\n        });\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.AppCompatTextView;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceCategory;\nimport androidx.preference.PreferenceScreen;\n\nimport awais.instagrabber.R;\n\npublic class AboutFragment extends BasePreferencesFragment {\n    private static AppCompatTextView customPathTextView;\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        final PreferenceCategory generalCategory = new PreferenceCategory(context);\n        screen.addPreference(generalCategory);\n        generalCategory.setTitle(R.string.pref_category_general);\n        generalCategory.setIconSpaceReserved(false);\n        generalCategory.addPreference(getDocsPreference());\n        generalCategory.addPreference(getRepoPreference());\n        generalCategory.addPreference(getFeedbackPreference());\n\n        final PreferenceCategory licenseCategory = new PreferenceCategory(context);\n        screen.addPreference(licenseCategory);\n        licenseCategory.setTitle(R.string.about_category_license);\n        licenseCategory.setIconSpaceReserved(false);\n        licenseCategory.addPreference(getLicensePreference());\n        licenseCategory.addPreference(getLiabilityPreference());\n\n        final PreferenceCategory thirdPartyCategory = new PreferenceCategory(context);\n        screen.addPreference(thirdPartyCategory);\n        thirdPartyCategory.setTitle(R.string.about_category_3pt);\n        thirdPartyCategory.setIconSpaceReserved(false);\n        // alphabetical order!!!\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"Apache Commons Imaging\",\n                \"Copyright 2007-2020 The Apache Software Foundation. Apache 2.0. This product includes software developed at The Apache Software Foundation (http://www.apache.org/).\",\n                \"https://commons.apache.org/proper/commons-imaging/\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"AutoLinkTextViewV2\",\n                \"Copyright (C) 2019 Arman Chatikyan. Apache 2.0.\",\n                \"https://github.com/armcha/AutoLinkTextViewV2\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"ExoPlayer\",\n                \"Copyright (C) 2016 The Android Open Source Project. Apache 2.0.\",\n                \"https://exoplayer.dev\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"Fresco\",\n                \"Copyright (c) Facebook, Inc. and its affiliates. MIT License.\",\n                \"https://frescolib.org\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"GPUImage\",\n                \"Copyright 2018 CyberAgent, Inc. Apache 2.0.\",\n                \"https://github.com/cats-oss/android-gpuimage\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"Material Design Icons\",\n                \"Copyright (C) 2014 Austin Andrews & Google LLC. Apache 2.0.\",\n                \"https://materialdesignicons.com\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"Process Phoenix\",\n                \"Copyright (C) 2015 Jake Wharton. Apache 2.0.\",\n                \"https://github.com/JakeWharton/ProcessPhoenix\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"Retrofit\",\n                \"Copyright 2013 Square, Inc. Apache 2.0.\",\n                \"https://square.github.io/retrofit/\"\n        ));\n        thirdPartyCategory.addPreference(get3ptPreference(\n                context,\n                \"uCrop\",\n                \"Copyright 2017 Yalantis. Apache 2.0.\",\n                \"https://github.com/Yalantis/uCrop\"\n        ));\n    }\n\n    private Preference getDocsPreference() {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.about_documentation);\n        preference.setSummary(R.string.about_documentation_summary);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(p -> {\n            final Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.setData(Uri.parse(\"https://barinsta.austinhuang.me\"));\n            startActivity(intent);\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getRepoPreference() {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.about_repository);\n        preference.setSummary(R.string.about_repository_summary);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(p -> {\n            final Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.setData(Uri.parse(\"https://github.com/austinhuang0131/barinsta\"));\n            startActivity(intent);\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getFeedbackPreference() {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.about_feedback);\n        preference.setSummary(R.string.about_feedback_summary);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(p -> {\n            final Intent intent = new Intent(Intent.ACTION_SEND);\n            intent.setType(\"message/rfc822\")\n                    .putExtra(Intent.EXTRA_EMAIL, getString(R.string.about_feedback_summary))\n                    .putExtra(Intent.EXTRA_TEXT, \"Please note that your email address and the entire content will be published onto GitHub issues. If you do not wish to do that, use other contact methods instead.\");\n            if (intent.resolveActivity(context.getPackageManager()) != null) startActivity(intent);\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getLicensePreference() {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        preference.setSummary(R.string.license);\n        preference.setEnabled(false);\n        preference.setIcon(R.drawable.ic_outline_info_24);\n        preference.setIconSpaceReserved(true);\n        return preference;\n    }\n\n    private Preference getLiabilityPreference() {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        preference.setSummary(R.string.liability);\n        preference.setEnabled(false);\n        preference.setIcon(R.drawable.ic_warning);\n        preference.setIconSpaceReserved(true);\n        return preference;\n    }\n\n    private Preference get3ptPreference(@NonNull final Context context,\n                                        final String title,\n                                        final String summary,\n                                        final String url) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(title);\n        preference.setSummary(summary);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(p -> {\n            final Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.setData(Uri.parse(url));\n            startActivity(intent);\n            return true;\n        });\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceCategory;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.SwitchPreferenceCompat;\n\nimport com.google.android.material.snackbar.BaseTransientBottomBar;\nimport com.google.android.material.snackbar.Snackbar;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.CreateBackupDialogFragment;\nimport awais.instagrabber.dialogs.RestoreBackupDialogFragment;\n\npublic class BackupPreferencesFragment extends BasePreferencesFragment {\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) {\n            return;\n        }\n        if (Build.VERSION.SDK_INT >= 23) {\n            final PreferenceCategory autoCategory = new PreferenceCategory(context);\n            screen.addPreference(autoCategory);\n            autoCategory.setTitle(R.string.auto_backup);\n            autoCategory.addPreference(getAboutPreference(context, true));\n            autoCategory.addPreference(getWarningPreference(context, true));\n            autoCategory.addPreference(getAutoBackupPreference(context));\n        }\n        final PreferenceCategory manualCategory = new PreferenceCategory(context);\n        screen.addPreference(manualCategory);\n        manualCategory.setTitle(R.string.manual_backup);\n        manualCategory.addPreference(getAboutPreference(context, false));\n        manualCategory.addPreference(getWarningPreference(context, false));\n        manualCategory.addPreference(getCreatePreference(context));\n        manualCategory.addPreference(getRestorePreference(context));\n    }\n\n    private Preference getAboutPreference(@NonNull final Context context,\n                                          @NonNull final boolean auto) {\n        final Preference preference = new Preference(context);\n        preference.setSummary(auto ? R.string.auto_backup_summary : R.string.backup_summary);\n        preference.setEnabled(false);\n        preference.setIcon(R.drawable.ic_outline_info_24);\n        preference.setIconSpaceReserved(true);\n        return preference;\n    }\n\n    private Preference getWarningPreference(@NonNull final Context context,\n                                            @NonNull final boolean auto) {\n        final Preference preference = new Preference(context);\n        preference.setSummary(auto ? R.string.auto_backup_warning : R.string.backup_warning);\n        preference.setEnabled(false);\n        preference.setIcon(R.drawable.ic_warning);\n        preference.setIconSpaceReserved(true);\n        return preference;\n    }\n\n    private Preference getAutoBackupPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.PREF_AUTO_BACKUP_ENABLED);\n        preference.setTitle(R.string.auto_backup_setting);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getCreatePreference(@NonNull final Context context) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.create_backup);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(preference1 -> {\n            final FragmentManager fragmentManager = getParentFragmentManager();\n            final CreateBackupDialogFragment fragment = new CreateBackupDialogFragment(result -> {\n                final View view = getView();\n                if (view != null) {\n                    Snackbar.make(view,\n                                  result ? R.string.dialog_export_success\n                                         : R.string.dialog_export_failed,\n                                  BaseTransientBottomBar.LENGTH_LONG)\n                            .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)\n                            .setAction(R.string.ok, v -> {})\n                            .show();\n                    return;\n                }\n                Toast.makeText(context,\n                               result ? R.string.dialog_export_success\n                                      : R.string.dialog_export_failed,\n                               Toast.LENGTH_LONG)\n                     .show();\n            });\n            final FragmentTransaction ft = fragmentManager.beginTransaction();\n            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)\n              .add(fragment, \"createBackup\")\n              .commit();\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getRestorePreference(@NonNull final Context context) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.restore_backup);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(preference1 -> {\n            final FragmentManager fragmentManager = getParentFragmentManager();\n            final RestoreBackupDialogFragment fragment = new RestoreBackupDialogFragment(result -> {\n                final View view = getView();\n                if (view != null) {\n                    Snackbar.make(view,\n                                  result ? R.string.dialog_import_success\n                                         : R.string.dialog_import_failed,\n                                  BaseTransientBottomBar.LENGTH_LONG)\n                            .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)\n                            .setAction(R.string.ok, v -> {})\n                            .addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {\n                                @Override\n                                public void onDismissed(final Snackbar transientBottomBar, final int event) {\n                                    recreateActivity(result);\n                                }\n                            })\n                            .show();\n                    return;\n                }\n                recreateActivity(result);\n            });\n            final FragmentTransaction ft = fragmentManager.beginTransaction();\n            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)\n              .add(fragment, \"restoreBackup\")\n              .commit();\n            return true;\n        });\n        return preference;\n    }\n\n    private void recreateActivity(final boolean result) {\n        if (!result) return;\n        final FragmentActivity activity = getActivity();\n        if (activity == null) return;\n        activity.recreate();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/BasePreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceFragmentCompat;\nimport androidx.preference.PreferenceManager;\nimport androidx.preference.PreferenceScreen;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.LocaleUtils;\n\npublic abstract class BasePreferencesFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {\n    private boolean shouldRecreate = false;\n\n    @Override\n    public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {\n        final PreferenceManager preferenceManager = getPreferenceManager();\n        preferenceManager.setSharedPreferencesName(Constants.SHARED_PREFERENCES_NAME);\n        preferenceManager.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);\n        final Context context = getContext();\n        if (context == null) return;\n        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);\n        setupPreferenceScreen(screen);\n        setPreferenceScreen(screen);\n    }\n\n    abstract void setupPreferenceScreen(PreferenceScreen screen);\n\n    protected void shouldRecreate() {\n        this.shouldRecreate = true;\n    }\n\n    @Override\n    public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {\n        if (!shouldRecreate) return;\n        final MainActivity activity = (MainActivity) getActivity();\n        if (activity == null) return;\n        if (key.equals(PreferenceKeys.APP_LANGUAGE)) {\n            LocaleUtils.setLocale(activity.getBaseContext());\n        }\n        shouldRecreate = false;\n        activity.recreate();\n    }\n\n    @NonNull\n    protected Preference getDivider(final Context context) {\n        final Preference divider = new Preference(context);\n        divider.setLayoutResource(R.layout.item_pref_divider);\n        return divider;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/DMPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.text.Editable;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.PreferenceViewHolder;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.customviews.helpers.TextWatcherAdapter;\nimport awais.instagrabber.databinding.PrefAutoRefreshDmFreqBinding;\nimport awais.instagrabber.services.DMSyncAlarmReceiver;\nimport awais.instagrabber.services.DMSyncService;\nimport awais.instagrabber.utils.Debouncer;\nimport awais.instagrabber.utils.TextUtils;\n\nimport static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER;\nimport static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT;\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class DMPreferencesFragment extends BasePreferencesFragment {\n    private static final String TAG = DMPreferencesFragment.class.getSimpleName();\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getMarkDMSeenPreference(context));\n        // screen.addPreference(getAutoRefreshDMPreference(context));\n        // screen.addPreference(getAutoRefreshDMFreqPreference(context));\n    }\n\n    private Preference getMarkDMSeenPreference(@NonNull final Context context) {\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PreferenceKeys.DM_MARK_AS_SEEN,\n                R.string.dm_mark_as_seen_setting,\n                R.string.dm_mark_as_seen_setting_summary,\n                false,\n                null\n        );\n    }\n\n    private Preference getAutoRefreshDMPreference(@NonNull final Context context) {\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH,\n                R.string.enable_dm_auto_refesh,\n                -1,\n                false,\n                (preference, newValue) -> {\n                    if (!(newValue instanceof Boolean)) return false;\n                    final boolean enabled = (Boolean) newValue;\n                    if (enabled) {\n                        DMSyncAlarmReceiver.setAlarm(context);\n                        return true;\n                    }\n                    DMSyncAlarmReceiver.cancelAlarm(context);\n                    try {\n                        final Context applicationContext = context.getApplicationContext();\n                        applicationContext.stopService(new Intent(applicationContext, DMSyncService.class));\n                    } catch (Exception e) {\n                        Log.e(TAG, \"getAutoRefreshDMPreference: \", e);\n                    }\n                    return true;\n                }\n        );\n    }\n\n    private Preference getAutoRefreshDMFreqPreference(@NonNull final Context context) {\n        return new AutoRefreshDMFrePreference(context);\n    }\n\n    public static class AutoRefreshDMFrePreference extends Preference {\n        private static final String TAG = AutoRefreshDMFrePreference.class.getSimpleName();\n        private static final String DEBOUNCE_KEY = \"dm_sync_service_update\";\n        public static final int INTERVAL = 2000;\n\n        private final Debouncer.Callback<String> changeCallback;\n\n        private Debouncer<String> serviceUpdateDebouncer;\n        private PrefAutoRefreshDmFreqBinding binding;\n\n        public AutoRefreshDMFrePreference(final Context context) {\n            super(context);\n            setLayoutResource(R.layout.pref_auto_refresh_dm_freq);\n            // setKey(key);\n            setIconSpaceReserved(false);\n            changeCallback = new Debouncer.Callback<String>() {\n                @Override\n                public void call(final String key) {\n                    DMSyncAlarmReceiver.setAlarm(context);\n                }\n\n                @Override\n                public void onError(final Throwable t) {\n                    Log.e(TAG, \"onError: \", t);\n                }\n            };\n            serviceUpdateDebouncer = new Debouncer<>(changeCallback, INTERVAL);\n        }\n\n        @Override\n        public void onDependencyChanged(final Preference dependency, final boolean disableDependent) {\n            // super.onDependencyChanged(dependency, disableDependent);\n            if (binding == null) return;\n            binding.startText.setEnabled(!disableDependent);\n            binding.freqNum.setEnabled(!disableDependent);\n            binding.freqUnit.setEnabled(!disableDependent);\n            if (disableDependent) {\n                serviceUpdateDebouncer.terminate();\n                return;\n            }\n            serviceUpdateDebouncer = new Debouncer<>(changeCallback, INTERVAL);\n        }\n\n        @Override\n        public void onBindViewHolder(final PreferenceViewHolder holder) {\n            super.onBindViewHolder(holder);\n            setDependency(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);\n            binding = PrefAutoRefreshDmFreqBinding.bind(holder.itemView);\n            final Context context = getContext();\n            if (context == null) return;\n            setupUnitSpinner(context);\n            setupNumberEditText(context);\n        }\n\n        private void setupUnitSpinner(final Context context) {\n            final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context,\n                                                                                       R.array.dm_auto_refresh_freq_unit_labels,\n                                                                                       android.R.layout.simple_spinner_item);\n            final String[] values = context.getResources().getStringArray(R.array.dm_auto_refresh_freq_units);\n            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\n            binding.freqUnit.setAdapter(adapter);\n\n            String unit = settingsHelper.getString(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT);\n            if (TextUtils.isEmpty(unit)) {\n                unit = \"secs\";\n            }\n            int position = 0;\n            for (int i = 0; i < values.length; i++) {\n                if (Objects.equals(unit, values[i])) {\n                    position = i;\n                    break;\n                }\n            }\n            binding.freqUnit.setSelection(position);\n            binding.freqUnit.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {\n                @Override\n                public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) {\n                    settingsHelper.putString(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT, values[position]);\n                    if (!isEnabled()) {\n                        serviceUpdateDebouncer.terminate();\n                        return;\n                    }\n                    serviceUpdateDebouncer.call(DEBOUNCE_KEY);\n                }\n\n                @Override\n                public void onNothingSelected(final AdapterView<?> parent) {}\n            });\n        }\n\n        private void setupNumberEditText(final Context context) {\n            int currentValue = settingsHelper.getInteger(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER);\n            if (currentValue <= 0) {\n                currentValue = 30;\n            }\n            binding.freqNum.setText(String.valueOf(currentValue));\n            binding.freqNum.addTextChangedListener(new TextWatcherAdapter() {\n\n                @Override\n                public void afterTextChanged(final Editable s) {\n                    if (TextUtils.isEmpty(s)) return;\n                    try {\n                        final int value = Integer.parseInt(s.toString());\n                        if (value <= 0) return;\n                        settingsHelper.putInteger(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER, value);\n                        if (!isEnabled()) {\n                            serviceUpdateDebouncer.terminate();\n                            return;\n                        }\n                        serviceUpdateDebouncer.call(DEBOUNCE_KEY);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"afterTextChanged: \", e);\n                    }\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.annotation.SuppressLint;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.DocumentsContract;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.SwitchPreferenceCompat;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.ConfirmDialogFragment;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\n\nimport static android.app.Activity.RESULT_OK;\nimport static awais.instagrabber.activities.DirectorySelectActivity.SELECT_DIR_REQUEST_CODE;\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class DownloadsPreferencesFragment extends BasePreferencesFragment {\n    private static final String TAG = DownloadsPreferencesFragment.class.getSimpleName();\n    private Preference dirPreference;\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getDownloadUserFolderPreference(context));\n        screen.addPreference(getSaveToCustomFolderPreference(context));\n        screen.addPreference(getPrependUsernameToFilenamePreference(context));\n    }\n\n    private Preference getDownloadUserFolderPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.DOWNLOAD_USER_FOLDER);\n        preference.setTitle(R.string.download_user_folder);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getSaveToCustomFolderPreference(@NonNull final Context context) {\n        dirPreference = new Preference(context);\n        dirPreference.setIconSpaceReserved(false);\n        dirPreference.setTitle(R.string.barinsta_folder);\n        final String currentValue = settingsHelper.getString(PreferenceKeys.PREF_BARINSTA_DIR_URI);\n        if (TextUtils.isEmpty(currentValue)) dirPreference.setSummary(\"\");\n        else {\n            String path;\n            try {\n                path = URLDecoder.decode(currentValue, StandardCharsets.UTF_8.toString());\n            } catch (UnsupportedEncodingException e) {\n                path = currentValue;\n            }\n            dirPreference.setSummary(path);\n        }\n        dirPreference.setOnPreferenceClickListener(p -> {\n            openDirectoryChooser(DownloadUtils.getRootDirUri());\n            return true;\n        });\n        return dirPreference;\n    }\n\n    private void openDirectoryChooser(final Uri initialUri) {\n        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {\n            intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri);\n        }\n        try {\n            startActivityForResult(intent, SELECT_DIR_REQUEST_CODE);\n        } catch (ActivityNotFoundException e) {\n            Log.e(TAG, \"openDirectoryChooser: \", e);\n            showErrorDialog(getString(R.string.no_directory_picker_activity));\n        } catch (Exception e) {\n            Log.e(TAG, \"openDirectoryChooser: \", e);\n        }\n    }\n\n    @SuppressLint(\"StringFormatInvalid\")\n    @Override\n    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {\n        if (requestCode != SELECT_DIR_REQUEST_CODE) return;\n        if (resultCode != RESULT_OK) return;\n        if (data == null || data.getData() == null) return;\n        final Context context = getContext();\n        if (context == null) return;\n        AppExecutors.INSTANCE.getMainThread().execute(() -> {\n            try {\n                Utils.setupSelectedDir(context, data);\n                String path;\n                try {\n                    path = URLDecoder.decode(data.getData().toString(), StandardCharsets.UTF_8.name());\n                } catch (UnsupportedEncodingException e) {\n                    path = data.getData().toString();\n                }\n                dirPreference.setSummary(path);\n            } catch (Exception e) {\n                // Should not come to this point.\n                // If it does, we have to show this error to the user so that they can report it.\n                try (final StringWriter sw = new StringWriter();\n                     final PrintWriter pw = new PrintWriter(sw)) {\n                    e.printStackTrace(pw);\n                    showErrorDialog(\"com.android.externalstorage.documents\".equals(data.getData().getAuthority())\n                                    ? \"Please report this error to the developers:\\n\\n\" + sw.toString()\n                                    : getString(R.string.dir_select_no_download_folder, data.getData().getAuthority()));\n                } catch (IOException ioException) {\n                    Log.e(TAG, \"onActivityResult: \", ioException);\n                }\n            }\n        }, 500);\n    }\n\n    private void showErrorDialog(final String message) {\n        final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(\n                123,\n                R.string.error,\n                message,\n                R.string.ok,\n                0,\n                0\n        );\n        dialogFragment.show(getChildFragmentManager(), ConfirmDialogFragment.class.getSimpleName());\n    }\n\n    private Preference getPrependUsernameToFilenamePreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME);\n        preference.setTitle(R.string.download_prepend_username);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.SwitchPreferenceCompat;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.ConfirmDialogFragment;\nimport awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment;\nimport awais.instagrabber.models.Tab;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.NavigationHelperKt;\nimport awais.instagrabber.utils.TextUtils;\nimport kotlin.Pair;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class GeneralPreferencesFragment extends BasePreferencesFragment implements TabOrderPreferenceDialogFragment.Callback {\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        if (isLoggedIn) {\n            screen.addPreference(getDefaultTabPreference(context));\n            screen.addPreference(getTabOrderPreference(context));\n        }\n        screen.addPreference(getDisableScreenTransitionsPreference(context));\n        screen.addPreference(getUpdateCheckPreference(context));\n        screen.addPreference(getFlagSecurePreference(context));\n        screen.addPreference(getSearchFocusPreference(context));\n        final List<Preference> preferences = FlavorSettings\n                .getInstance()\n                .getPreferences(\n                        context,\n                        getChildFragmentManager(),\n                        SettingCategory.GENERAL\n                );\n        for (final Preference preference : preferences) {\n            screen.addPreference(preference);\n        }\n    }\n\n    private Preference getDefaultTabPreference(@NonNull final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final Pair<List<Tab>, List<Tab>> listPair = NavigationHelperKt.getLoggedInNavTabs(context);\n        final List<Tab> tabs = listPair.getFirst();\n        final String[] titles = tabs.stream()\n                                    .map(Tab::getTitle)\n                                    .toArray(String[]::new);\n        final String[] navGraphFileNames = tabs.stream()\n                                               .map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))\n                                               .toArray(String[]::new);\n        preference.setKey(Constants.DEFAULT_TAB);\n        preference.setTitle(R.string.pref_start_screen);\n        preference.setDialogTitle(R.string.pref_start_screen);\n        preference.setEntries(titles);\n        preference.setEntryValues(navGraphFileNames);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    @NonNull\n    private Preference getTabOrderPreference(@NonNull final Context context) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.tab_order);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(preference1 -> {\n            final TabOrderPreferenceDialogFragment dialogFragment = TabOrderPreferenceDialogFragment.newInstance();\n            dialogFragment.show(getChildFragmentManager(), \"tab_order_dialog\");\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getDisableScreenTransitionsPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS);\n        preference.setTitle(R.string.disable_screen_transitions);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getUpdateCheckPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.CHECK_UPDATES);\n        preference.setTitle(R.string.update_check);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getFlagSecurePreference(@NonNull final Context context) {\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PreferenceKeys.FLAG_SECURE,\n                R.string.flag_secure,\n                -1,\n                false,\n                (preference, newValue) -> {\n                    shouldRecreate();\n                    return true;\n                });\n    }\n\n    private Preference getSearchFocusPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.PREF_SEARCH_FOCUS_KEYBOARD);\n        preference.setTitle(R.string.pref_search_focus_keyboard);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    @Override\n    public void onSave(final boolean orderHasChanged) {\n        if (!orderHasChanged) return;\n        final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(\n                111,\n                0,\n                R.string.tab_order_start_next_launch,\n                R.string.ok,\n                0,\n                0);\n        dialogFragment.show(getChildFragmentManager(), \"tab_order_set_dialog\");\n    }\n\n    @Override\n    public void onCancel() {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.fragment.app.FragmentManager;\nimport androidx.preference.Preference;\n\nimport java.util.List;\n\npublic interface IFlavorSettings {\n    List<Preference> getPreferences(Context context,\n                                    FragmentManager childFragmentManager,\n                                    SettingCategory settingCategory);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/LocalePreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\n\nimport java.time.format.DateTimeFormatter;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.TimeSettingsDialog;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.LocaleUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.UserAgentUtils;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class LocalePreferencesFragment extends BasePreferencesFragment {\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getLanguagePreference(context));\n        screen.addPreference(getPostTimeFormatPreference(context));\n    }\n\n    private Preference getLanguagePreference(@NonNull final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final int length = getResources().getStringArray(R.array.languages).length;\n        final String[] values = new String[length];\n        for (int i = 0; i < length; i++) {\n            values[i] = String.valueOf(i);\n        }\n        preference.setKey(PreferenceKeys.APP_LANGUAGE);\n        preference.setTitle(R.string.select_language);\n        preference.setDialogTitle(R.string.select_language);\n        preference.setEntries(R.array.languages);\n        preference.setIconSpaceReserved(false);\n        preference.setEntryValues(values);\n        preference.setOnPreferenceChangeListener((preference1, newValue) -> {\n            shouldRecreate();\n            final int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);\n            final String appUa = UserAgentUtils.generateAppUA(appUaCode, LocaleUtils.getCurrentLocale().getLanguage());\n            settingsHelper.putString(Constants.APP_UA, appUa);\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getPostTimeFormatPreference(@NonNull final Context context) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.time_settings);\n        preference.setSummary(TextUtils.nowToString());\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(preference1 -> {\n            new TimeSettingsDialog(\n                    settingsHelper.getBoolean(PreferenceKeys.CUSTOM_DATE_TIME_FORMAT_ENABLED),\n                    settingsHelper.getString(PreferenceKeys.CUSTOM_DATE_TIME_FORMAT),\n                    settingsHelper.getString(PreferenceKeys.DATE_TIME_SELECTION),\n                    settingsHelper.getBoolean(PreferenceKeys.SWAP_DATE_TIME_FORMAT_ENABLED),\n                    (isCustomFormat,\n                     spTimeFormatSelectedItemPosition,\n                     spSeparatorSelectedItemPosition,\n                     spDateFormatSelectedItemPosition,\n                     selectedFormat,\n                     swapDateTime) -> {\n                        settingsHelper.putBoolean(PreferenceKeys.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);\n                        settingsHelper.putBoolean(PreferenceKeys.SWAP_DATE_TIME_FORMAT_ENABLED, swapDateTime);\n                        if (isCustomFormat) {\n                            settingsHelper.putString(PreferenceKeys.CUSTOM_DATE_TIME_FORMAT, selectedFormat);\n                        } else {\n                            final String formatSelectionUpdated = spTimeFormatSelectedItemPosition + \";\"\n                                    + spSeparatorSelectedItemPosition + ';'\n                                    + spDateFormatSelectedItemPosition; // time;separator;date\n                            settingsHelper.putString(PreferenceKeys.DATE_TIME_FORMAT, selectedFormat);\n                            settingsHelper.putString(PreferenceKeys.DATE_TIME_SELECTION, formatSelectionUpdated);\n                        }\n                        TextUtils.setFormatter(DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale()));\n                        preference.setSummary(TextUtils.nowToString());\n                    }\n            ).show(getParentFragmentManager(), null);\n            return true;\n        });\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Resources;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.navigation.NavController;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceCategory;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.PreferenceViewHolder;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.BuildConfig;\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.Login;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.databinding.PrefAccountSwitcherBinding;\nimport awais.instagrabber.db.repositories.AccountRepository;\nimport awais.instagrabber.dialogs.AccountSwitcherDialogFragment;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.FlavorTown;\nimport awais.instagrabber.utils.NavigationHelperKt;\nimport awais.instagrabber.utils.ProcessPhoenix;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.webservices.UserRepository;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class MorePreferencesFragment extends BasePreferencesFragment {\n    private static final String TAG = \"MorePreferencesFragment\";\n\n    private AccountRepository accountRepository;\n\n    public MorePreferencesFragment() {\n    }\n\n    @Override\n    public RecyclerView onCreateRecyclerView(final LayoutInflater inflater, final ViewGroup parent, final Bundle savedInstanceState) {\n        final RecyclerView recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState);\n        final Context context = getContext();\n        if (recyclerView != null && context != null) {\n            recyclerView.setClipToPadding(false);\n            recyclerView.setPadding(recyclerView.getPaddingLeft(),\n                                    recyclerView.getPaddingTop(),\n                                    recyclerView.getPaddingRight(),\n                                    Utils.getActionBarHeight(context));\n        }\n        return recyclerView;\n    }\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        final MainActivity activity = (MainActivity) getActivity();\n        // screen.addPreference(new MoreHeaderPreference(getContext()));\n        final Context context = getContext();\n        if (context == null) return;\n        accountRepository = AccountRepository.Companion.getInstance(context);\n        final PreferenceCategory accountCategory = new PreferenceCategory(context);\n        accountCategory.setTitle(R.string.account);\n        accountCategory.setIconSpaceReserved(false);\n        screen.addPreference(accountCategory);\n        if (isLoggedIn) {\n            accountCategory.setSummary(R.string.account_hint);\n            accountCategory.addPreference(getAccountSwitcherPreference(cookie, context));\n            accountCategory.addPreference(getPreference(R.string.logout, R.string.logout_summary, R.drawable.ic_logout_24, preference -> {\n                final Context context1 = getContext();\n                if (context1 == null) return false;\n                CookieUtils.setupCookies(\"LOGOUT\");\n                // shouldRecreate();\n                Toast.makeText(context1, R.string.logout_success, Toast.LENGTH_SHORT).show();\n                settingsHelper.putString(Constants.COOKIE, \"\");\n                AppExecutors.INSTANCE.getMainThread().execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);\n                return true;\n            }));\n        }\n        accountRepository.getAllAccounts(\n                CoroutineUtilsKt.getContinuation((accounts, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.d(TAG, \"getAllAccounts\", throwable);\n                        if (!isLoggedIn) {\n                            // Need to show something to trigger login activity\n                            accountCategory.addPreference(getPreference(R.string.add_account, R.drawable.ic_add, preference -> {\n                                startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE);\n                                return true;\n                            }));\n                        }\n                        return;\n                    }\n                    if (!isLoggedIn) {\n                        if (accounts.size() > 0) {\n                            final Context context1 = getContext();\n                            final AccountSwitcherPreference preference = getAccountSwitcherPreference(null, context1);\n                            if (preference == null) return;\n                            accountCategory.addPreference(preference);\n                        }\n                        // Need to show something to trigger login activity\n                        final Preference preference1 = getPreference(R.string.add_account, R.drawable.ic_add, preference -> {\n                            final Context context1 = getContext();\n                            if (context1 == null) return false;\n                            startActivityForResult(new Intent(context1, Login.class), Constants.LOGIN_RESULT_CODE);\n                            return true;\n                        });\n                        if (preference1 == null) return;\n                        accountCategory.addPreference(preference1);\n                    }\n                    if (accounts.size() > 0) {\n                        final Preference preference1 = getPreference(\n                                R.string.remove_all_acc,\n                                null,\n                                R.drawable.ic_account_multiple_remove_24,\n                                preference -> {\n                                    if (getContext() == null) return false;\n                                    new AlertDialog.Builder(getContext())\n                                            .setTitle(R.string.logout)\n                                            .setMessage(R.string.remove_all_acc_warning)\n                                            .setPositiveButton(R.string.yes, (dialog, which) -> {\n                                                final Context context1 = getContext();\n                                                if (context1 == null) return;\n                                                CookieUtils.removeAllAccounts(\n                                                        context1,\n                                                        CoroutineUtilsKt.getContinuation(\n                                                                (unit, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                                                    if (throwable1 != null) {\n                                                                        return;\n                                                                    }\n                                                                    final Context context2 = getContext();\n                                                                    if (context2 == null) return;\n                                                                    Toast.makeText(context2, R.string.logout_success, Toast.LENGTH_SHORT).show();\n                                                                    settingsHelper.putString(Constants.COOKIE, \"\");\n                                                                    AppExecutors.INSTANCE\n                                                                            .getMainThread()\n                                                                            .execute(() -> ProcessPhoenix.triggerRebirth(context1), 200);\n                                                                }),\n                                                                Dispatchers.getIO()\n                                                        )\n                                                );\n                                            })\n                                            .setNegativeButton(R.string.cancel, null)\n                                            .show();\n                                    return true;\n                                });\n                        if (preference1 == null) return;\n                        accountCategory.addPreference(preference1);\n                    }\n                }), Dispatchers.getIO())\n        );\n\n        // final PreferenceCategory generalCategory = new PreferenceCategory(context);\n        // generalCategory.setTitle(R.string.pref_category_general);\n        // generalCategory.setIconSpaceReserved(false);\n        // screen.addPreference(generalCategory);\n        screen.addPreference(getDivider(context));\n        final NavController navController = NavHostFragment.findNavController(this);\n        if (isLoggedIn) {\n            boolean showActivity = true;\n            boolean showExplore = false;\n            if (activity != null) {\n                showActivity = !NavigationHelperKt.isNavRootInCurrentTabs(\"notification_viewer_nav_graph\");\n                showExplore = !NavigationHelperKt.isNavRootInCurrentTabs(\"discover_nav_graph\");\n            }\n            if (showActivity) {\n                screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {\n                    if (isSafeToNavigate(navController)) {\n                        try {\n                            final NavDirections navDirections = MorePreferencesFragmentDirections.actionToNotifications().setType(\"notif\");\n                            navController.navigate(navDirections);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"setupPreferenceScreen: \", e);\n                        }\n                    }\n                    return true;\n                }));\n            }\n            if (showExplore) {\n                screen.addPreference(getPreference(R.string.title_discover, R.drawable.ic_explore_24, preference -> {\n                    if (isSafeToNavigate(navController)) {\n                        try {\n                            final NavDirections navDirections = MorePreferencesFragmentDirections.actionToDiscover();\n                            navController.navigate(navDirections);\n                        } catch (Exception e) {\n                            Log.e(TAG, \"setupPreferenceScreen: \", e);\n                        }\n                    }\n                    return true;\n                }));\n            }\n\n            screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {\n                if (isSafeToNavigate(navController)) {\n                    try {\n                        final NavDirections navDirections = MorePreferencesFragmentDirections.actionToNotifications().setType(\"ayml\");\n                        navController.navigate(navDirections);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"setupPreferenceScreen: \", e);\n                    }\n                }\n                return true;\n            }));\n            screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {\n                if (isSafeToNavigate(navController)) {\n                    try {\n                        final NavDirections navDirections = MorePreferencesFragmentDirections.actionToStoryList(\"archive\");\n                        navController.navigate(navDirections);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"setupPreferenceScreen: \", e);\n                    }\n                }\n                return true;\n            }));\n        }\n\n        // Check if favorites has been added as a tab. And if so, do not add in this list\n        boolean showFavorites = true;\n        if (activity != null) {\n            showFavorites = !NavigationHelperKt.isNavRootInCurrentTabs(\"favorites_nav_graph\");\n        }\n        if (showFavorites) {\n            screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {\n                if (isSafeToNavigate(navController)) {\n                    try {\n                        final NavDirections navDirections = MorePreferencesFragmentDirections.actionToFavorites();\n                        navController.navigate(navDirections);\n                    } catch (Exception e) {\n                        Log.e(TAG, \"setupPreferenceScreen: \", e);\n                    }\n                }\n                return true;\n            }));\n        }\n\n        screen.addPreference(getDivider(context));\n        screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {\n            if (isSafeToNavigate(navController)) {\n                try {\n                    final NavDirections navDirections = MorePreferencesFragmentDirections.actionToSettings();\n                    navController.navigate(navDirections);\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupPreferenceScreen: \", e);\n                }\n            }\n            return true;\n        }));\n        screen.addPreference(getPreference(R.string.backup_and_restore, R.drawable.ic_settings_backup_restore_24, preference -> {\n            if (isSafeToNavigate(navController)) {\n                try {\n                    final NavDirections navDirections = MorePreferencesFragmentDirections.actionToBackup();\n                    navController.navigate(navDirections);\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupPreferenceScreen: \", e);\n                }\n            }\n            return true;\n        }));\n        screen.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference1 -> {\n            if (isSafeToNavigate(navController)) {\n                try {\n                    final NavDirections navDirections = MorePreferencesFragmentDirections.actionToAbout();\n                    navController.navigate(navDirections);\n                } catch (Exception e) {\n                    Log.e(TAG, \"setupPreferenceScreen: \", e);\n                }\n            }\n            return true;\n        }));\n\n        screen.addPreference(getDivider(context));\n        screen.addPreference(getPreference(\n                R.string.version,\n                BuildConfig.VERSION_NAME + \" (\" + BuildConfig.VERSION_CODE + \")\",\n                -1,\n                preference -> {\n                    if (BuildConfig.isPre) return true;\n                    if (activity == null) return false;\n                    FlavorTown.updateCheck(activity, true);\n                    return true;\n                })\n        );\n        screen.addPreference(getDivider(context));\n\n        final Preference reminderPreference = getPreference(R.string.reminder, R.string.reminder_summary, R.drawable.ic_warning, null);\n        if (reminderPreference == null) return;\n        reminderPreference.setSelectable(false);\n        screen.addPreference(reminderPreference);\n    }\n\n    private boolean isSafeToNavigate(final NavController navController) {\n        return navController.getCurrentDestination() != null\n                && navController.getCurrentDestination().getId() == R.id.morePreferencesFragment;\n    }\n\n    @Override\n    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {\n        if (resultCode == Constants.LOGIN_RESULT_CODE) {\n            if (data == null) return;\n            final String cookie = data.getStringExtra(\"cookie\");\n            CookieUtils.setupCookies(cookie);\n            settingsHelper.putString(Constants.COOKIE, cookie);\n            // No use as the timing of show is unreliable\n            // Toast.makeText(getContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show();\n\n            // adds cookies to database for quick access\n            final long uid = CookieUtils.getUserIdFromCookie(cookie);\n            final UserRepository userRepository = UserRepository.Companion.getInstance();\n            userRepository\n                    .getUserInfo(uid, CoroutineUtilsKt.getContinuation((user, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            Log.e(TAG, \"Error fetching user info\", throwable);\n                            return;\n                        }\n                        if (user != null) {\n                            accountRepository.insertOrUpdateAccount(\n                                    uid,\n                                    user.getUsername(),\n                                    cookie,\n                                    user.getFullName(),\n                                    user.getProfilePicUrl(),\n                                    CoroutineUtilsKt.getContinuation((account, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                        if (throwable1 != null) {\n                                            Log.e(TAG, \"onActivityResult: \", throwable1);\n                                            return;\n                                        }\n                                        AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                                            final Context context = getContext();\n                                            if (context == null) return;\n                                            ProcessPhoenix.triggerRebirth(context);\n                                        }, 200);\n                                    }), Dispatchers.getIO())\n                            );\n                        }\n                    }), Dispatchers.getIO()));\n        }\n    }\n\n    @Nullable\n    private AccountSwitcherPreference getAccountSwitcherPreference(final String cookie, final Context context) {\n        if (context == null) return null;\n        return new AccountSwitcherPreference(context, cookie, accountRepository, v -> showAccountSwitcherDialog());\n    }\n\n    private void showAccountSwitcherDialog() {\n        final AccountSwitcherDialogFragment dialogFragment = new AccountSwitcherDialogFragment(dialog -> {\n            dialog.dismiss();\n            startActivityForResult(new Intent(getContext(), Login.class), Constants.LOGIN_RESULT_CODE);\n        });\n        final FragmentManager fragmentManager = getChildFragmentManager();\n        dialogFragment.show(fragmentManager, \"accountSwitcher\");\n    }\n\n    @Nullable\n    private Preference getPreference(final int title,\n                                     final int icon,\n                                     final Preference.OnPreferenceClickListener clickListener) {\n        return getPreference(title, -1, icon, clickListener);\n    }\n\n    @Nullable\n    private Preference getPreference(final int title,\n                                     final int summary,\n                                     final int icon,\n                                     final Preference.OnPreferenceClickListener clickListener) {\n        String string = null;\n        if (summary > 0) {\n            try {\n                string = getString(summary);\n            } catch (Resources.NotFoundException e) {\n                Log.e(TAG, \"Error\", e);\n            }\n        }\n        return getPreference(title, string, icon, clickListener);\n    }\n\n    @Nullable\n    private Preference getPreference(final int title,\n                                     final String summary,\n                                     final int icon,\n                                     final Preference.OnPreferenceClickListener clickListener) {\n        final Context context = getContext();\n        if (context == null) return null;\n        final Preference preference = new Preference(context);\n        if (icon <= 0) preference.setIconSpaceReserved(false);\n        if (icon > 0) preference.setIcon(icon);\n        preference.setTitle(title);\n        if (!TextUtils.isEmpty(summary)) {\n            preference.setSummary(summary);\n        }\n        preference.setOnPreferenceClickListener(clickListener);\n        return preference;\n    }\n\n    // public static class MoreHeaderPreference extends Preference {\n    //\n    //     public MoreHeaderPreference(final Context context) {\n    //         super(context);\n    //         setLayoutResource(R.layout.pref_more_header);\n    //         setSelectable(false);\n    //     }\n    // }\n\n    public static class AccountSwitcherPreference extends Preference {\n\n        private final String cookie;\n        private final AccountRepository accountRepository;\n        private final View.OnClickListener onClickListener;\n\n        public AccountSwitcherPreference(final Context context,\n                                         final String cookie,\n                                         final AccountRepository accountRepository,\n                                         final View.OnClickListener onClickListener) {\n            super(context);\n            this.cookie = cookie;\n            this.accountRepository = accountRepository;\n            this.onClickListener = onClickListener;\n            setLayoutResource(R.layout.pref_account_switcher);\n        }\n\n        @SuppressLint(\"SetTextI18n\")\n        @Override\n        public void onBindViewHolder(final PreferenceViewHolder holder) {\n            final View root = holder.itemView;\n            if (onClickListener != null) root.setOnClickListener(onClickListener);\n            final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.bind(root);\n            final long uid = CookieUtils.getUserIdFromCookie(cookie);\n            if (uid <= 0) return;\n            accountRepository.getAccount(\n                    uid,\n                    CoroutineUtilsKt.getContinuation((account, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            Log.e(TAG, \"onBindViewHolder: \", throwable);\n                            return;\n                        }\n                        if (account == null) return;\n                        binding.getRoot().post(() -> {\n                            binding.fullName.setText(account.getFullName());\n                            binding.username.setText(\"@\" + account.getUsername());\n                            binding.profilePic.setImageURI(account.getProfilePic());\n                            binding.getRoot().requestLayout();\n                        });\n                    }), Dispatchers.getIO())\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/NotificationsPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\n\nimport awais.instagrabber.R;\n\npublic class NotificationsPreferencesFragment extends BasePreferencesFragment {\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getActivityNotificationsPreference(context));\n        // screen.addPreference(getDMNotificationsPreference(context));\n    }\n\n    private Preference getActivityNotificationsPreference(@NonNull final Context context) {\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PreferenceKeys.CHECK_ACTIVITY,\n                R.string.activity_setting,\n                -1,\n                false,\n                (preference, newValue) -> {\n                    shouldRecreate();\n                    return true;\n                });\n    }\n\n    private Preference getDMNotificationsPreference(@NonNull final Context context) {\n        return PreferenceHelper.getSwitchPreference(\n                context,\n                PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS,\n                R.string.enable_dm_notifications,\n                -1,\n                false,\n                null);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.SwitchPreferenceCompat;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.dialogs.KeywordsFilterDialog;\n\npublic class PostPreferencesFragment extends BasePreferencesFragment {\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        // generalCategory.addPreference(getAutoPlayVideosPreference(context));\n        screen.addPreference(getBackgroundPlayPreference(context));\n        screen.addPreference(getAlwaysMuteVideosPreference(context));\n        screen.addPreference(getToggleKeywordFilterPreference(context));\n        screen.addPreference(getEditKeywordFilterPreference(context));\n    }\n\n//    private Preference getAutoPlayVideosPreference(@NonNull final Context context) {\n//        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n//        preference.setKey(Constants.AUTOPLAY_VIDEOS);\n//        preference.setTitle(R.string.post_viewer_autoplay_video);\n//        preference.setIconSpaceReserved(false);\n//        return preference;\n//    }\n\n    private Preference getBackgroundPlayPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.PLAY_IN_BACKGROUND);\n        preference.setTitle(R.string.post_viewer_background_play);\n        preference.setSummary(R.string.post_viewer_background_play_summary);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getAlwaysMuteVideosPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.MUTED_VIDEOS);\n        preference.setTitle(R.string.post_viewer_muted_autoplay);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getToggleKeywordFilterPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.TOGGLE_KEYWORD_FILTER);\n        preference.setDefaultValue(false);\n        preference.setTitle(R.string.toggle_keyword_filter);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getEditKeywordFilterPreference(@NonNull final Context context){\n        final Preference preference = new Preference(context);\n        preference.setTitle(R.string.edit_keyword_filter);\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(view ->{\n            new KeywordsFilterDialog().show(getParentFragmentManager(), null);\n            return true;\n        });\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/PreferenceHelper.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.preference.Preference.OnPreferenceChangeListener;\nimport androidx.preference.SwitchPreferenceCompat;\n\npublic final class PreferenceHelper {\n\n    public static SwitchPreferenceCompat getSwitchPreference(@NonNull final Context context,\n                                                             @NonNull final String key,\n                                                             @StringRes final int titleResId,\n                                                             @StringRes final int summaryResId,\n                                                             final boolean iconSpaceReserved,\n                                                             final OnPreferenceChangeListener onPreferenceChangeListener) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(key);\n        preference.setTitle(titleResId);\n        preference.setIconSpaceReserved(iconSpaceReserved);\n        if (summaryResId != -1) {\n            preference.setSummary(summaryResId);\n        }\n        if (onPreferenceChangeListener != null) {\n            preference.setOnPreferenceChangeListener(onPreferenceChangeListener);\n        }\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.kt",
    "content": "package awais.instagrabber.fragments.settings\n\nobject PreferenceKeys {\n    // new boolean prefs\n    const val PREF_ENABLE_DM_NOTIFICATIONS = \"enable_dm_notifications\"\n    const val PREF_ENABLE_DM_AUTO_REFRESH = \"enable_dm_auto_refresh\"\n    const val PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = \"enable_dm_auto_refresh_freq_unit\"\n    const val PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = \"enable_dm_auto_refresh_freq_number\"\n    const val PREF_ENABLE_SENTRY = \"enable_sentry\"\n    const val PREF_TAB_ORDER = \"tab_order\"\n    const val PREF_SHOWN_COUNT_TOOLTIP = \"shown_count_tooltip\"\n    const val PREF_SEARCH_FOCUS_KEYBOARD = \"search_focus_keyboard\"\n    const val PREF_AUTO_BACKUP_ENABLED = \"auto_backup_enabled\"\n    const val PREF_DISABLE_SCREEN_TRANSITIONS = \"disable_screen_transitions\"\n    const val PREF_STORY_SHOW_LIST = \"story_show_list\"\n\n    // string prefs\n    const val FOLDER_PATH = \"custom_path\"\n    const val DATE_TIME_FORMAT = \"date_time_format\"\n    const val DATE_TIME_SELECTION = \"date_time_selection\"\n    const val CUSTOM_DATE_TIME_FORMAT = \"date_time_custom_format\"\n    const val APP_THEME = \"app_theme_v19\"\n    const val APP_LANGUAGE = \"app_language_v19\"\n    const val STORY_SORT = \"story_sort\"\n    const val PREF_BARINSTA_DIR_URI = \"barinsta_dir_uri\"\n\n    // set string prefs\n    const val KEYWORD_FILTERS = \"keyword_filters\"\n\n    // old boolean prefs\n    const val DOWNLOAD_USER_FOLDER = \"download_user_folder\"\n    const val TOGGLE_KEYWORD_FILTER = \"toggle_keyword_filter\"\n    const val DOWNLOAD_PREPEND_USER_NAME = \"download_user_name\"\n    const val PLAY_IN_BACKGROUND = \"play_in_background\"\n    const val AUTOPLAY_VIDEOS_STORIES = \"autoplay_videos\"\n    const val MUTED_VIDEOS = \"muted_videos\"\n//    const val SHOW_CAPTIONS = \"show_captions\"\n    const val CUSTOM_DATE_TIME_FORMAT_ENABLED = \"data_time_custom_enabled\"\n    const val SWAP_DATE_TIME_FORMAT_ENABLED = \"swap_date_time_enabled\"\n    const val MARK_AS_SEEN = \"mark_as_seen\"\n    const val HIDE_MUTED_REELS = \"hide_muted_reels\"\n    const val DM_MARK_AS_SEEN = \"dm_mark_as_seen\"\n    const val CHECK_ACTIVITY = \"check_activity\"\n    const val CHECK_UPDATES = \"check_updates\"\n    const val FLAG_SECURE = \"flag_secure\"\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java",
    "content": "package awais.instagrabber.fragments.settings;\n\npublic enum SettingCategory {\n    GENERAL,\n    // add more as and when required\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.navigation.NavDirections;\nimport androidx.navigation.fragment.NavHostFragment;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.TextUtils;\n\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToDm;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToDownloads;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToGeneral;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToLocale;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToNotifications;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToPost;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToStories;\nimport static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToTheme;\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class SettingsPreferencesFragment extends BasePreferencesFragment {\n    private static final String TAG = SettingsPreferencesFragment.class.getSimpleName();\n    private static final List<SettingScreen> screens = ImmutableList.of(\n            new SettingScreen(R.string.pref_category_general, actionSettingsToGeneral()),\n            new SettingScreen(R.string.pref_category_theme, actionSettingsToTheme()),\n            new SettingScreen(R.string.pref_category_locale, actionSettingsToLocale()),\n            new SettingScreen(R.string.pref_category_post, actionSettingsToPost()),\n            new SettingScreen(R.string.pref_category_stories, actionSettingsToStories(), true),\n            new SettingScreen(R.string.pref_category_dm, actionSettingsToDm(), true),\n            new SettingScreen(R.string.pref_category_notifications, actionSettingsToNotifications(), true),\n            new SettingScreen(R.string.pref_category_downloads, actionSettingsToDownloads())\n    );\n\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;\n        for (final SettingScreen settingScreen : screens) {\n            if (settingScreen.isLoginRequired() && !isLoggedIn) continue;\n            screen.addPreference(getNavPreference(context, settingScreen));\n        }\n        //        else {\n        //            final PreferenceCategory anonUsersPreferenceCategory = new PreferenceCategory(context);\n        //            screen.addPreference(anonUsersPreferenceCategory);\n        //            anonUsersPreferenceCategory.setIconSpaceReserved(false);\n        //            anonUsersPreferenceCategory.setTitle(R.string.anonymous_settings);\n        //        }\n    }\n\n    private Preference getNavPreference(@NonNull final Context context,\n                                        @NonNull final SettingScreen settingScreen) {\n        final Preference preference = new Preference(context);\n        preference.setTitle(settingScreen.getTitleResId());\n        preference.setIconSpaceReserved(false);\n        preference.setOnPreferenceClickListener(preference1 -> {\n            NavHostFragment.findNavController(this).navigate(settingScreen.getDirections());\n            return true;\n        });\n        return preference;\n    }\n\n    private static class SettingScreen {\n        private final int titleResId;\n        private final NavDirections directions;\n        private final boolean loginRequired;\n\n        public SettingScreen(@StringRes final int titleResId, final NavDirections directions) {\n            this(titleResId, directions, false);\n        }\n\n        public SettingScreen(@StringRes final int titleResId, final NavDirections directions, final boolean loginRequired) {\n            this.titleResId = titleResId;\n            this.directions = directions;\n            this.loginRequired = loginRequired;\n        }\n\n        public int getTitleResId() {\n            return titleResId;\n        }\n\n        public NavDirections getDirections() {\n            return directions;\n        }\n\n        public boolean isLoginRequired() {\n            return loginRequired;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\nimport androidx.preference.SwitchPreferenceCompat;\n\nimport awais.instagrabber.R;\n\npublic class StoriesPreferencesFragment extends BasePreferencesFragment {\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getStorySortPreference(context));\n        screen.addPreference(getHideMutedReelsPreference(context));\n        screen.addPreference(getMarkStoriesSeenPreference(context));\n        screen.addPreference(getAutoPlayPreference(context));\n        screen.addPreference(getStoryListPreference(context));\n    }\n\n    private Preference getStorySortPreference(@NonNull final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final int length = getResources().getStringArray(R.array.story_sorts).length;\n        final String[] values = new String[length];\n        for (int i = 0; i < length; i++) {\n            values[i] = String.valueOf(i);\n        }\n        preference.setKey(PreferenceKeys.STORY_SORT);\n        preference.setTitle(R.string.story_sort_setting);\n        preference.setDialogTitle(R.string.story_sort_setting);\n        preference.setEntries(R.array.story_sorts);\n        preference.setIconSpaceReserved(false);\n        preference.setEntryValues(values);\n        return preference;\n    }\n\n    private Preference getHideMutedReelsPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.HIDE_MUTED_REELS);\n        preference.setTitle(R.string.hide_muted_reels_setting);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getMarkStoriesSeenPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.MARK_AS_SEEN);\n        preference.setTitle(R.string.mark_as_seen_setting);\n        preference.setSummary(R.string.mark_as_seen_setting_summary);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getAutoPlayPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.AUTOPLAY_VIDEOS_STORIES);\n        preference.setTitle(R.string.autoplay_stories_setting);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n\n    private Preference getStoryListPreference(@NonNull final Context context) {\n        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);\n        preference.setKey(PreferenceKeys.PREF_STORY_SHOW_LIST);\n        preference.setTitle(R.string.story_list_setting);\n        preference.setSummary(R.string.story_list_setting_summary);\n        preference.setIconSpaceReserved(false);\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/fragments/settings/ThemePreferencesFragment.java",
    "content": "package awais.instagrabber.fragments.settings;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\n\nimport androidx.annotation.NonNull;\nimport androidx.preference.ListPreference;\nimport androidx.preference.Preference;\nimport androidx.preference.PreferenceScreen;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.utils.Constants;\n\npublic class ThemePreferencesFragment extends BasePreferencesFragment {\n    @Override\n    void setupPreferenceScreen(final PreferenceScreen screen) {\n        final Context context = getContext();\n        if (context == null) return;\n        screen.addPreference(getThemePreference(context));\n        screen.addPreference(getLightThemePreference(context));\n        screen.addPreference(getDarkThemePreference(context));\n    }\n\n    private Preference getThemePreference(@NonNull final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final int length = getResources().getStringArray(R.array.theme_presets).length;\n        final String[] values = new String[length];\n        for (int i = 0; i < length; i++) {\n            values[i] = String.valueOf(i);\n        }\n        preference.setKey(PreferenceKeys.APP_THEME);\n        preference.setTitle(R.string.theme_settings);\n        preference.setDialogTitle(R.string.theme_settings);\n        preference.setEntries(R.array.theme_presets);\n        preference.setIconSpaceReserved(false);\n        preference.setEntryValues(values);\n        preference.setOnPreferenceChangeListener((preference1, newValue) -> {\n            shouldRecreate();\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getLightThemePreference(final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final TypedArray lightThemeValues = getResources().obtainTypedArray(R.array.light_theme_values);\n        final int length = lightThemeValues.length();\n        final String[] values = new String[length];\n        for (int i = 0; i < length; i++) {\n            final int resourceId = lightThemeValues.getResourceId(i, -1);\n            if (resourceId < 0) continue;\n            values[i] = getResources().getResourceEntryName(resourceId);\n        }\n        lightThemeValues.recycle();\n        preference.setKey(Constants.PREF_LIGHT_THEME);\n        preference.setTitle(R.string.light_theme_settings);\n        preference.setDialogTitle(R.string.light_theme_settings);\n        preference.setEntries(R.array.light_themes);\n        preference.setIconSpaceReserved(false);\n        preference.setEntryValues(values);\n        preference.setOnPreferenceChangeListener((preference1, newValue) -> {\n            shouldRecreate();\n            return true;\n        });\n        return preference;\n    }\n\n    private Preference getDarkThemePreference(final Context context) {\n        final ListPreference preference = new ListPreference(context);\n        preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());\n        final TypedArray darkThemeValues = getResources().obtainTypedArray(R.array.dark_theme_values);\n        final int length = darkThemeValues.length();\n        final String[] values = new String[length];\n        for (int i = 0; i < length; i++) {\n            final int resourceId = darkThemeValues.getResourceId(i, -1);\n            if (resourceId < 0) continue;\n            values[i] = getResources().getResourceEntryName(resourceId);\n        }\n        darkThemeValues.recycle();\n        preference.setKey(Constants.PREF_DARK_THEME);\n        preference.setTitle(R.string.dark_theme_settings);\n        preference.setDialogTitle(R.string.dark_theme_settings);\n        preference.setEntries(R.array.dark_themes);\n        preference.setIconSpaceReserved(false);\n        preference.setEntryValues(values);\n        preference.setOnPreferenceChangeListener((preference1, newValue) -> {\n            shouldRecreate();\n            return true;\n        });\n        return preference;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/interfaces/FetchListener.java",
    "content": "package awais.instagrabber.interfaces;\n\npublic interface FetchListener<T> {\n    void onResult(T result);\n\n    default void doBefore() {}\n\n    default void onFailure(Throwable t) {}\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/interfaces/LazyLoadListener.java",
    "content": "package awais.instagrabber.interfaces;\n\npublic interface LazyLoadListener {\n    void onLoadMore(final int page, final int totalItemsCount);\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/interfaces/OnGroupClickListener.java",
    "content": "package awais.instagrabber.interfaces;\n\npublic interface OnGroupClickListener {\n    void toggleGroup(final int flatPos);\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/interfaces/SwipeEvent.java",
    "content": "package awais.instagrabber.interfaces;\n\npublic interface SwipeEvent {\n    void onSwipe(final boolean isRight);\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt",
    "content": "package awais.instagrabber.managers\n\nimport android.content.ContentResolver\nimport android.util.Log\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.getCsrfTokenFromCookie\nimport awais.instagrabber.utils.getUserIdFromCookie\nimport awais.instagrabber.webservices.DirectMessagesRepository\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport java.util.*\n\nobject DirectMessagesManager {\n    val inboxManager: InboxManager by lazy { InboxManager(false) }\n    val pendingInboxManager: InboxManager by lazy { InboxManager(true) }\n\n    private val TAG = DirectMessagesManager::class.java.simpleName\n    private val viewerId: Long\n    private val deviceUuid: String\n    private val csrfToken: String\n    private val directMessagesRepository by lazy { DirectMessagesRepository.getInstance() }\n\n    fun moveThreadFromPending(threadId: String) {\n        val pendingThreads = pendingInboxManager.threads.value ?: return\n        val index = pendingThreads.indexOfFirst { it.threadId == threadId }\n        if (index < 0) return\n        val thread = pendingThreads[index]\n        val threadFirstDirectItem = thread.firstDirectItem ?: return\n        val threads = inboxManager.threads.value\n        var insertIndex = 0\n        if (threads != null) {\n            for (tempThread in threads) {\n                val firstDirectItem = tempThread.firstDirectItem ?: continue\n                val timestamp = firstDirectItem.getTimestamp()\n                if (timestamp < threadFirstDirectItem.getTimestamp()) {\n                    break\n                }\n                insertIndex++\n            }\n        }\n        thread.pending = false\n        inboxManager.addThread(thread, insertIndex)\n        pendingInboxManager.removeThread(threadId)\n        val currentTotal = inboxManager.getPendingRequestsTotal().value ?: return\n        inboxManager.setPendingRequestsTotal(currentTotal - 1)\n    }\n\n    fun getThreadManager(\n        threadId: String,\n        pending: Boolean,\n        currentUser: User,\n        contentResolver: ContentResolver,\n    ): ThreadManager {\n        return ThreadManager(threadId, pending, currentUser, contentResolver, viewerId, csrfToken, deviceUuid)\n    }\n\n    suspend fun createThread(userPk: Long): DirectThread =\n        directMessagesRepository.createThread(csrfToken, viewerId, deviceUuid, listOf(userPk), null)\n\n    fun sendMedia(recipient: RankedRecipient, mediaId: String, secondId: String?, itemType: BroadcastItemType, scope: CoroutineScope) {\n        sendMedia(setOf(recipient), mediaId, secondId, itemType, scope)\n    }\n\n    fun sendMedia(\n        recipients: Set<RankedRecipient>,\n        mediaId: String,\n        secondId: String?,\n        itemType: BroadcastItemType,\n        scope: CoroutineScope,\n    ) {\n        val threadIds = recipients.mapNotNull { it.thread?.threadId }\n        val userIdsTemp = recipients.mapNotNull { it.user?.pk }\n        val userIds = userIdsTemp.map { listOf(it.toString(10)) }\n        sendMedia(threadIds, userIds, mediaId, secondId, itemType, scope) {\n            inboxManager.refresh(scope)\n        }\n    }\n\n    private fun sendMedia(\n        threadIds: List<String>,\n        userIds: List<List<String>>,\n        mediaId: String,\n        secondId: String?,\n        itemType: BroadcastItemType,\n        scope: CoroutineScope,\n        callback: (() -> Unit)?,\n    ): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                when (itemType) {\n                    BroadcastItemType.MEDIA_SHARE -> directMessagesRepository.broadcastMediaShare(\n                        csrfToken,\n                        viewerId,\n                        deviceUuid,\n                        UUID.randomUUID().toString(),\n                        ThreadIdsOrUserIds(threadIds, userIds),\n                        mediaId,\n                        secondId\n                    )\n                    BroadcastItemType.PROFILE -> directMessagesRepository.broadcastProfile(\n                        csrfToken,\n                        viewerId,\n                        deviceUuid,\n                        UUID.randomUUID().toString(),\n                        ThreadIdsOrUserIds(threadIds, userIds),\n                        mediaId\n                    )\n                    BroadcastItemType.STORY -> directMessagesRepository.broadcastStory(\n                        csrfToken,\n                        viewerId,\n                        deviceUuid,\n                        UUID.randomUUID().toString(),\n                        ThreadIdsOrUserIds(threadIds, userIds),\n                        mediaId,\n                        secondId!!\n                    )\n                }\n                data.postValue(success(Any()))\n                callback?.invoke()\n            } catch (e: Exception) {\n                Log.e(TAG, \"sendMedia: \", e)\n                data.postValue(error(e.message, null))\n                callback?.invoke()\n            }\n        }\n        return data\n    }\n\n    fun replyToStory(\n        recipientId: Long?,\n        reelId: String?,\n        mediaId: String?,\n        text: String,\n        scope: CoroutineScope\n    ): LiveData<Resource<Any?>> {\n        Log.d(\"austin_debug\", \"replying\")\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (recipientId == null || reelId == null || mediaId == null) {\n            data.postValue(error(\"arguments are null\", null))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.broadcastStoryReply(\n                    csrfToken,\n                    viewerId,\n                    deviceUuid,\n                    ThreadIdsOrUserIds.Companion.ofOneUser(recipientId.toString(10)),\n                    text,\n                    mediaId,\n                    reelId\n                )\n                inboxManager.refresh(scope)\n                data.postValue(success(null))\n            }\n            catch (e: Exception) {\n                Log.e(TAG, \"story reply: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    init {\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        viewerId = getUserIdFromCookie(cookie)\n        deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        val csrfToken = getCsrfTokenFromCookie(cookie)\n        require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { \"User is not logged in!\" }\n        this.csrfToken = csrfToken\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/managers/InboxManager.kt",
    "content": "package awais.instagrabber.managers\n\nimport android.util.Log\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Transformations\nimport awais.instagrabber.R\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.*\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.webservices.DirectMessagesRepository\nimport com.google.common.cache.CacheBuilder\nimport com.google.common.cache.CacheLoader\nimport com.google.common.collect.ImmutableList\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport retrofit2.Call\nimport java.util.*\nimport java.util.concurrent.TimeUnit\n\nclass InboxManager(private val pending: Boolean) {\n    private val directMessagesRepository by lazy { DirectMessagesRepository.getInstance() }\n    private val inbox = MutableLiveData<Resource<DirectInbox?>>(success(null))\n    private val unseenCount = MutableLiveData<Resource<Int?>>()\n    private val pendingRequestsTotal = MutableLiveData(0)\n    val threads: LiveData<List<DirectThread>>\n    private var inboxRequest: Call<DirectInboxResponse?>? = null\n    private var unseenCountRequest: Call<DirectBadgeCount?>? = null\n    private var seqId: Long = 0\n    private var cursor: String? = null\n    private var hasOlder = true\n    var viewer: User? = null\n        private set\n\n    fun getInbox(): LiveData<Resource<DirectInbox?>> {\n        return Transformations.distinctUntilChanged(inbox)\n    }\n\n    fun getUnseenCount(): LiveData<Resource<Int?>> {\n        return Transformations.distinctUntilChanged(unseenCount)\n    }\n\n    fun getPendingRequestsTotal(): LiveData<Int> {\n        return Transformations.distinctUntilChanged(pendingRequestsTotal)\n    }\n\n    fun fetchInbox(scope: CoroutineScope) {\n        val inboxResource = inbox.value\n        if (inboxResource != null && inboxResource.status === Resource.Status.LOADING || !hasOlder) return\n        inbox.postValue(loading(currentDirectInbox))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val inboxValue = if (pending) {\n                    directMessagesRepository.fetchPendingInbox(cursor, seqId)\n                } else {\n                    directMessagesRepository.fetchInbox(cursor, seqId)\n                }\n                parseInboxResponse(inboxValue)\n            } catch (e: Exception) {\n                inbox.postValue(error(e.message, currentDirectInbox))\n                hasOlder = false\n            }\n        }\n    }\n\n    fun fetchUnseenCount(scope: CoroutineScope) {\n        val unseenCountResource = unseenCount.value\n        if (unseenCountResource != null && unseenCountResource.status === Resource.Status.LOADING) return\n        stopCurrentUnseenCountRequest()\n        unseenCount.postValue(loading(currentUnseenCount))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val directBadgeCount = directMessagesRepository.fetchUnseenCount()\n                unseenCount.postValue(success(directBadgeCount.badgeCount))\n            } catch (e: Exception) {\n                Log.e(TAG, \"Failed fetching unseen count\", e)\n                unseenCount.postValue(error(e.message, currentUnseenCount))\n            }\n        }\n    }\n\n    fun refresh(scope: CoroutineScope) {\n        cursor = null\n        seqId = 0\n        hasOlder = true\n        fetchInbox(scope)\n        if (!pending) {\n            fetchUnseenCount(scope)\n        }\n    }\n\n    private val currentDirectInbox: DirectInbox?\n        get() {\n            val inboxResource = inbox.value\n            return inboxResource?.data\n        }\n\n    private fun parseInboxResponse(response: DirectInboxResponse) {\n        if (response.status != \"ok\") {\n            Log.e(TAG, \"DM inbox fetch response: status not ok\")\n            inbox.postValue(error(R.string.generic_not_ok_response, currentDirectInbox))\n            hasOlder = false\n            return\n        }\n        seqId = response.seqId\n        if (viewer == null) {\n            viewer = response.viewer\n        }\n        val inbox = response.inbox ?: return\n        if (!cursor.isNullOrBlank()) {\n            val currentDirectInbox = currentDirectInbox\n            currentDirectInbox?.let {\n                val threads = it.threads\n                val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)\n                threadsCopy.addAll(inbox.threads ?: emptyList())\n                inbox.threads = threadsCopy\n            }\n        }\n        this.inbox.postValue(success(inbox))\n        cursor = inbox.oldestCursor\n        hasOlder = inbox.hasOlder\n        pendingRequestsTotal.postValue(response.pendingRequestsTotal)\n    }\n\n    fun setThread(\n        threadId: String,\n        thread: DirectThread,\n    ) {\n        val inbox = currentDirectInbox ?: return\n        val index = getThreadIndex(threadId, inbox)\n        setThread(inbox, index, thread)\n    }\n\n    private fun setThread(\n        inbox: DirectInbox,\n        index: Int,\n        thread: DirectThread,\n    ) {\n        if (index < 0) return\n        synchronized(this.inbox) {\n            val threads = inbox.threads\n            val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)\n            threadsCopy[index] = thread\n            try {\n                val clone = inbox.clone() as DirectInbox\n                clone.threads = threadsCopy\n                this.inbox.postValue(success(clone))\n            } catch (e: CloneNotSupportedException) {\n                Log.e(TAG, \"setThread: \", e)\n            }\n        }\n    }\n\n    fun addItemsToThread(\n        threadId: String,\n        insertIndex: Int,\n        items: Collection<DirectItem>,\n    ) {\n        val inbox = currentDirectInbox ?: return\n        synchronized(THREAD_LOCKS.getUnchecked(threadId)) {\n            val index = getThreadIndex(threadId, inbox)\n            if (index < 0) return\n            val threads = inbox.threads ?: return\n            val thread = threads[index]\n            val threadItems = thread.items\n            val list = if (threadItems == null) LinkedList() else LinkedList(threadItems)\n            if (insertIndex >= 0) {\n                list.addAll(insertIndex, items)\n            } else {\n                list.addAll(items)\n            }\n            try {\n                val threadClone = thread.clone() as DirectThread\n                threadClone.items = list\n                setThread(inbox, index, threadClone)\n            } catch (e: Exception) {\n                Log.e(TAG, \"addItemsToThread: \", e)\n            }\n        }\n    }\n\n    fun setItemsToThread(\n        threadId: String,\n        updatedItems: List<DirectItem>,\n    ) {\n        val inbox = currentDirectInbox ?: return\n        synchronized(THREAD_LOCKS.getUnchecked(threadId)) {\n            val index = getThreadIndex(threadId, inbox)\n            if (index < 0) return\n            val threads = inbox.threads ?: return\n            val thread = threads[index]\n            try {\n                val threadClone = thread.clone() as DirectThread\n                threadClone.items = updatedItems\n                setThread(inbox, index, threadClone)\n            } catch (e: Exception) {\n                Log.e(TAG, \"setItemsToThread: \", e)\n            }\n        }\n    }\n\n    private fun getThreadIndex(\n        threadId: String,\n        inbox: DirectInbox,\n    ): Int {\n        val threads = inbox.threads\n        return if (threads == null || threads.isEmpty()) {\n            -1\n        } else threads.indexOfFirst { it.threadId == threadId }\n    }\n\n    private val currentUnseenCount: Int?\n        get() {\n            val unseenCountResource = unseenCount.value\n            return unseenCountResource?.data\n        }\n\n    private fun stopCurrentInboxRequest() {\n        inboxRequest?.let {\n            if (it.isCanceled || it.isExecuted) return\n            it.cancel()\n        }\n        inboxRequest = null\n    }\n\n    private fun stopCurrentUnseenCountRequest() {\n        unseenCountRequest?.let {\n            if (it.isCanceled || it.isExecuted) return\n            it.cancel()\n        }\n        unseenCountRequest = null\n    }\n\n    fun onDestroy() {\n        stopCurrentInboxRequest()\n        stopCurrentUnseenCountRequest()\n    }\n\n    fun addThread(thread: DirectThread, insertIndex: Int) {\n        if (insertIndex < 0) return\n        synchronized(inbox) {\n            val currentDirectInbox = currentDirectInbox ?: return\n            val threads = currentDirectInbox.threads\n            val threadsCopy = if (threads == null) LinkedList() else LinkedList(threads)\n            threadsCopy.add(insertIndex, thread)\n            try {\n                val clone = currentDirectInbox.clone() as DirectInbox\n                clone.threads = threadsCopy\n                inbox.postValue(success(clone))\n            } catch (e: CloneNotSupportedException) {\n                Log.e(TAG, \"setThread: \", e)\n            }\n        }\n    }\n\n    fun removeThread(threadId: String) {\n        synchronized(inbox) {\n            val currentDirectInbox = currentDirectInbox ?: return\n            val threads = currentDirectInbox.threads ?: return\n            val threadsCopy = threads.asSequence().filter { it.threadId != threadId }.toList()\n            try {\n                val clone = currentDirectInbox.clone() as DirectInbox\n                clone.threads = threadsCopy\n                inbox.postValue(success(clone))\n            } catch (e: CloneNotSupportedException) {\n                Log.e(TAG, \"setThread: \", e)\n            }\n        }\n    }\n\n    fun setPendingRequestsTotal(total: Int) {\n        pendingRequestsTotal.postValue(total)\n    }\n\n    fun containsThread(threadId: String?): Boolean {\n        if (threadId == null) return false\n        synchronized(inbox) {\n            val currentDirectInbox = currentDirectInbox ?: return false\n            val threads = currentDirectInbox.threads ?: return false\n            return threads.any { it.threadId == threadId }\n        }\n    }\n\n    companion object {\n        private val THREAD_LOCKS = CacheBuilder\n            .newBuilder()\n            .expireAfterAccess(1, TimeUnit.MINUTES) // max lock time ever expected\n            .build<String, Any>(CacheLoader.from<Any> { Object() })\n        private val THREAD_COMPARATOR = Comparator { t1: DirectThread, t2: DirectThread ->\n            val t1FirstDirectItem = t1.firstDirectItem\n            val t2FirstDirectItem = t2.firstDirectItem\n            if (t1FirstDirectItem == null && t2FirstDirectItem == null) return@Comparator 0\n            if (t1FirstDirectItem == null) return@Comparator 1\n            if (t2FirstDirectItem == null) return@Comparator -1\n            t2FirstDirectItem.getTimestamp().compareTo(t1FirstDirectItem.getTimestamp())\n        }\n    }\n\n    init {\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        val viewerId = getUserIdFromCookie(cookie)\n        val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        val csrfToken = getCsrfTokenFromCookie(cookie)\n        require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { \"User is not logged in!\" }\n\n        // Transformations\n        threads = Transformations.distinctUntilChanged(Transformations.map(inbox) { inboxResource: Resource<DirectInbox?> ->\n            // if (inboxResource == null) {\n            //     return@map emptyList()\n            // }\n            val inbox = inboxResource.data\n            val threads = inbox?.threads ?: emptyList()\n            ImmutableList.sortedCopyOf(THREAD_COMPARATOR, threads)\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/managers/ThreadManager.kt",
    "content": "package awais.instagrabber.managers\n\nimport android.content.ContentResolver\nimport android.net.Uri\nimport android.util.Log\nimport androidx.core.util.Pair\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Transformations.distinctUntilChanged\nimport androidx.lifecycle.Transformations.map\nimport awais.instagrabber.R\nimport awais.instagrabber.customviews.emoji.Emoji\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.models.enums.DirectItemType\nimport awais.instagrabber.repositories.requests.UploadFinishOptions\nimport awais.instagrabber.repositories.requests.VideoOptions\nimport awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds\nimport awais.instagrabber.repositories.requests.directmessages.ThreadIdsOrUserIds.Companion.of\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.*\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.MediaUploader.MediaUploadResponse\nimport awais.instagrabber.utils.MediaUploader.uploadPhoto\nimport awais.instagrabber.utils.MediaUploader.uploadVideo\nimport awais.instagrabber.utils.MediaUtils.OnInfoLoadListener\nimport awais.instagrabber.utils.MediaUtils.VideoInfo\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.webservices.DirectMessagesRepository\nimport awais.instagrabber.webservices.FriendshipRepository\nimport awais.instagrabber.webservices.MediaRepository\nimport com.google.common.collect.ImmutableList\nimport com.google.common.collect.Iterables\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport retrofit2.Call\nimport java.io.IOException\nimport java.net.HttpURLConnection\nimport java.util.*\nimport java.util.stream.Collectors\n\nclass ThreadManager(\n    private val threadId: String,\n    pending: Boolean,\n    private val currentUser: User?,\n    private val contentResolver: ContentResolver,\n    private val viewerId: Long,\n    private val csrfToken: String,\n    private val deviceUuid: String,\n) {\n    private val _fetching = MutableLiveData<Resource<Any?>>()\n    val fetching: LiveData<Resource<Any?>> = _fetching\n    private val _replyToItem = MutableLiveData<DirectItem?>()\n    val replyToItem: LiveData<DirectItem?> = _replyToItem\n    private val _pendingRequests = MutableLiveData<DirectThreadParticipantRequestsResponse?>(null)\n    val pendingRequests: LiveData<DirectThreadParticipantRequestsResponse?> = _pendingRequests\n    private val inboxManager: InboxManager = if (pending) DirectMessagesManager.pendingInboxManager else DirectMessagesManager.inboxManager\n    private val threadIdsOrUserIds: ThreadIdsOrUserIds = of(threadId)\n    private val friendshipRepository: FriendshipRepository by lazy { FriendshipRepository.getInstance() }\n    private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }\n    private val directMessagesRepository by lazy { DirectMessagesRepository.getInstance() }\n\n    val thread: LiveData<DirectThread?> by lazy {\n        distinctUntilChanged(map(inboxManager.getInbox()) { inboxResource: Resource<DirectInbox?>? ->\n            if (inboxResource == null) return@map null\n            val (threads) = inboxResource.data ?: return@map null\n            if (threads.isNullOrEmpty()) return@map null\n            val thread = threads.firstOrNull { it.threadId == threadId }\n            thread?.also {\n                cursor = thread.oldestCursor\n                hasOlder = thread.hasOlder\n            }\n        })\n    }\n    val inputMode: LiveData<Int> by lazy { distinctUntilChanged(map(thread) { it?.inputMode ?: 1 }) }\n    val threadTitle: LiveData<String?> by lazy { distinctUntilChanged(map(thread) { it?.threadTitle }) }\n    val users: LiveData<List<User>> by lazy { distinctUntilChanged(map(thread) { it?.users ?: emptyList() }) }\n    val usersWithCurrent: LiveData<List<User>> by lazy {\n        distinctUntilChanged(map(thread) {\n            if (it == null) return@map emptyList()\n            getUsersWithCurrentUser(it)\n        })\n    }\n    val leftUsers: LiveData<List<User>> by lazy { distinctUntilChanged(map(thread) { it?.leftUsers ?: emptyList() }) }\n    val usersAndLeftUsers: LiveData<Pair<List<User>, List<User>>> by lazy {\n        distinctUntilChanged(map(thread) {\n            if (it == null) return@map Pair<List<User>, List<User>>(emptyList(), emptyList())\n            val users = getUsersWithCurrentUser(it)\n            val leftUsers = it.leftUsers\n            Pair(users, leftUsers)\n        })\n    }\n    val isPending: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.pending ?: true }) }\n    val adminUserIds: LiveData<List<Long>> by lazy { distinctUntilChanged(map(thread) { it?.adminUserIds ?: emptyList() }) }\n    val items: LiveData<List<DirectItem>> by lazy { distinctUntilChanged(map(thread) { it?.items ?: emptyList() }) }\n    val isViewerAdmin: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.adminUserIds?.contains(viewerId) ?: false }) }\n    val isGroup: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.isGroup ?: false }) }\n    val isMuted: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.muted ?: false }) }\n    val isApprovalRequiredToJoin: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.approvalRequiredForNewMembers ?: false }) }\n    val isMentionsMuted: LiveData<Boolean> by lazy { distinctUntilChanged(map(thread) { it?.mentionsMuted ?: false }) }\n    val pendingRequestsCount: LiveData<Int> by lazy { distinctUntilChanged(map(_pendingRequests) { it?.totalParticipantRequests ?: 0 }) }\n    val inviter: LiveData<User?> by lazy { distinctUntilChanged(map(thread) { it?.inviter }) }\n\n    private var hasOlder = true\n    private var cursor: String? = null\n    private var chatsRequest: Call<DirectThreadFeedResponse?>? = null\n\n    private fun getUsersWithCurrentUser(t: DirectThread): List<User> {\n        val builder = ImmutableList.builder<User>()\n        if (currentUser != null) {\n            builder.add(currentUser)\n        }\n        val users: List<User>? = t.users\n        if (users != null) {\n            builder.addAll(users)\n        }\n        return builder.build()\n    }\n\n    fun fetchChats(scope: CoroutineScope) {\n        val fetchingValue = _fetching.value\n        if (fetchingValue != null && fetchingValue.status === Resource.Status.LOADING || !hasOlder) return\n        _fetching.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val threadFeedResponse = directMessagesRepository.fetchThread(threadId, cursor)\n                if (threadFeedResponse.status != null && threadFeedResponse.status != \"ok\") {\n                    _fetching.postValue(error(R.string.generic_not_ok_response, null))\n                    return@launch\n                }\n                val thread = threadFeedResponse.thread\n                if (thread == null) {\n                    _fetching.postValue(error(\"thread is null!\", null))\n                    return@launch\n                }\n                setThread(thread)\n                _fetching.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"Failed fetching dm chats\", e)\n                _fetching.postValue(error(e.message, null))\n                hasOlder = false\n            }\n        }\n        if (cursor == null) {\n            fetchPendingRequests(scope)\n        }\n    }\n\n    fun fetchPendingRequests(scope: CoroutineScope) {\n        val isGroup = isGroup.value\n        if (isGroup == null || !isGroup) return\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.participantRequests(threadId, 1)\n                _pendingRequests.postValue(response)\n            } catch (e: Exception) {\n                Log.e(TAG, \"fetchPendingRequests: \", e)\n            }\n        }\n    }\n\n    private fun setThread(thread: DirectThread, skipItems: Boolean) {\n        // if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) {\n        //     fetchPendingRequests();\n        // }\n        val items = thread.items\n        if (skipItems) {\n            val currentThread = this.thread.value\n            if (currentThread != null) {\n                thread.items = currentThread.items\n            }\n        }\n        if (!skipItems && !cursor.isNullOrBlank()) {\n            val currentThread = this.thread.value\n            if (currentThread != null) {\n                val currentItems = currentThread.items\n                val list = if (currentItems == null) LinkedList() else LinkedList(currentItems)\n                if (items != null) {\n                    list.addAll(items)\n                }\n                thread.items = list\n            }\n        }\n        inboxManager.setThread(threadId, thread)\n    }\n\n    private fun setThread(thread: DirectThread) {\n        setThread(thread, false)\n    }\n\n    private fun setThreadUsers(users: List<User>?, leftUsers: List<User>?) {\n        val currentThread = thread.value ?: return\n        val thread: DirectThread = try {\n            currentThread.clone() as DirectThread\n        } catch (e: CloneNotSupportedException) {\n            Log.e(TAG, \"setThreadUsers: \", e)\n            return\n        }\n        if (users != null) {\n            thread.users = users\n        }\n        if (leftUsers != null) {\n            thread.leftUsers = leftUsers\n        }\n        inboxManager.setThread(threadId, thread)\n    }\n\n    private fun addItems(index: Int, items: Collection<DirectItem>) {\n        inboxManager.addItemsToThread(threadId, index, items)\n    }\n\n    private fun addReaction(item: DirectItem, emoji: Emoji) {\n        if (currentUser == null) return\n        val isLike = emoji.unicode == \"❤️\"\n        var reactions = item.reactions\n        reactions = if (reactions == null) {\n            DirectItemReactions(null, null)\n        } else {\n            try {\n                reactions.clone() as DirectItemReactions\n            } catch (e: CloneNotSupportedException) {\n                Log.e(TAG, \"addReaction: \", e)\n                return\n            }\n        }\n        if (isLike) {\n            val likes = addEmoji(reactions.likes, null, false)\n            reactions.likes = likes\n        }\n        val emojis = addEmoji(reactions.emojis, emoji.unicode, true)\n        reactions.emojis = emojis\n        val currentItems = items.value\n        val items = if (currentItems == null) LinkedList() else LinkedList(currentItems)\n        val index = getItemIndex(item, items)\n        if (index >= 0) {\n            try {\n                val clone = items[index].clone() as DirectItem\n                clone.reactions = reactions\n                items[index] = clone\n            } catch (e: CloneNotSupportedException) {\n                Log.e(TAG, \"addReaction: error cloning\", e)\n            }\n        }\n        inboxManager.setItemsToThread(threadId, items)\n    }\n\n    private fun removeReaction(item: DirectItem) {\n        try {\n            val itemClone = item.clone() as DirectItem\n            val reactions = itemClone.reactions\n            var reactionsClone: DirectItemReactions? = null\n            if (reactions != null) {\n                reactionsClone = reactions.clone() as DirectItemReactions\n            }\n            var likes: List<DirectItemEmojiReaction>? = null\n            if (reactionsClone != null) {\n                likes = reactionsClone.likes\n            }\n            if (likes != null) {\n                val updatedLikes = likes.stream()\n                    .filter { (senderId) -> senderId != viewerId }\n                    .collect(Collectors.toList())\n                if (reactionsClone != null) {\n                    reactionsClone.likes = updatedLikes\n                }\n            }\n            var emojis: List<DirectItemEmojiReaction>? = null\n            if (reactionsClone != null) {\n                emojis = reactionsClone.emojis\n            }\n            if (emojis != null) {\n                val updatedEmojis = emojis.stream()\n                    .filter { (senderId) -> senderId != viewerId }\n                    .collect(Collectors.toList())\n                if (reactionsClone != null) {\n                    reactionsClone.emojis = updatedEmojis\n                }\n            }\n            itemClone.reactions = reactionsClone\n            val items = items.value\n            val list = if (items == null) LinkedList() else LinkedList(items)\n            val index = getItemIndex(item, list)\n            if (index >= 0) {\n                list[index] = itemClone\n            }\n            inboxManager.setItemsToThread(threadId, list)\n        } catch (e: Exception) {\n            Log.e(TAG, \"removeReaction: \", e)\n        }\n    }\n\n    private fun removeItem(item: DirectItem): Int {\n        val items = items.value\n        val list = if (items == null) LinkedList() else LinkedList(items)\n        val index = getItemIndex(item, list)\n        if (index >= 0) {\n            list.removeAt(index)\n            inboxManager.setItemsToThread(threadId, list)\n        }\n        return index\n    }\n\n    private fun addEmoji(\n        reactionList: List<DirectItemEmojiReaction>?,\n        emoji: String?,\n        shouldReplaceIfAlreadyReacted: Boolean,\n    ): List<DirectItemEmojiReaction>? {\n        if (currentUser == null) return reactionList\n        val temp: MutableList<DirectItemEmojiReaction> = if (reactionList == null) ArrayList() else ArrayList(reactionList)\n        var index = -1\n        for (i in temp.indices) {\n            val (senderId) = temp[i]\n            if (senderId == currentUser.pk) {\n                index = i\n                break\n            }\n        }\n        val reaction = DirectItemEmojiReaction(\n            currentUser.pk,\n            System.currentTimeMillis() * 1000,\n            emoji,\n            \"none\"\n        )\n        if (index < 0) {\n            temp.add(0, reaction)\n        } else if (shouldReplaceIfAlreadyReacted) {\n            temp[index] = reaction\n        }\n        return temp\n    }\n\n    fun sendText(text: String, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val userId = getCurrentUserId(data) ?: return data\n        val clientContext = UUID.randomUUID().toString()\n        val replyToItemValue = _replyToItem.value\n        val directItem = createText(userId, clientContext, text, replyToItemValue)\n        // Log.d(TAG, \"sendText: sending: itemId: \" + directItem.getItemId());\n        directItem.isPending = true\n        addItems(0, listOf(directItem))\n        data.postValue(loading(directItem))\n        val repliedToItemId = replyToItemValue?.itemId\n        val repliedToClientContext = replyToItemValue?.clientContext\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.broadcastText(\n                    csrfToken,\n                    viewerId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    text,\n                    repliedToItemId,\n                    repliedToClientContext\n                )\n                parseResponse(response, data, directItem)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, directItem))\n                Log.e(TAG, \"sendText: \", e)\n            }\n        }\n        return data\n    }\n\n    fun sendUri(uri: Uri, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val mimeType = Utils.getMimeType(uri, contentResolver)\n        if (isEmpty(mimeType)) {\n            data.postValue(error(\"Unknown MediaType\", null))\n            return data\n        }\n        val isPhoto = mimeType != null && mimeType.startsWith(\"image\")\n        if (isPhoto) {\n            sendPhoto(data, uri, scope)\n            return data\n        }\n        if (mimeType != null && mimeType.startsWith(\"video\")) {\n            sendVideo(data, uri, scope)\n        }\n        return data\n    }\n\n    fun sendAnimatedMedia(giphyGif: GiphyGif, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val userId = getCurrentUserId(data) ?: return data\n        val clientContext = UUID.randomUUID().toString()\n        val directItem = createAnimatedMedia(userId, clientContext, giphyGif)\n        directItem.isPending = true\n        addItems(0, listOf(directItem))\n        data.postValue(loading(directItem))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val request = directMessagesRepository.broadcastAnimatedMedia(\n                    csrfToken,\n                    userId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    giphyGif\n                )\n                parseResponse(request, data, directItem)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, directItem))\n                Log.e(TAG, \"sendAnimatedMedia: \", e)\n            }\n        }\n        return data\n    }\n\n    fun sendVoice(\n        data: MutableLiveData<Resource<Any?>>,\n        uri: Uri,\n        waveform: List<Float>,\n        samplingFreq: Int,\n        duration: Long,\n        byteLength: Long,\n        scope: CoroutineScope,\n    ) {\n        if (duration > 60000) {\n            // instagram does not allow uploading audio longer than 60 secs for Direct messages\n            data.postValue(error(R.string.dms_ERROR_AUDIO_TOO_LONG, null))\n            return\n        }\n        val userId = getCurrentUserId(data) ?: return\n        val clientContext = UUID.randomUUID().toString()\n        val directItem = createVoice(userId, clientContext, uri, duration, waveform, samplingFreq)\n        directItem.isPending = true\n        addItems(0, listOf(directItem))\n        data.postValue(loading(directItem))\n        val uploadDmVoiceOptions = createUploadDmVoiceOptions(byteLength, duration)\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = uploadVideo(uri, contentResolver, uploadDmVoiceOptions)\n                // Log.d(TAG, \"onUploadComplete: \" + response);\n                if (handleInvalidResponse(data, response)) return@launch\n                val uploadFinishOptions = UploadFinishOptions(\n                    uploadDmVoiceOptions.uploadId,\n                    \"4\",\n                    null\n                )\n                mediaRepository.uploadFinish(csrfToken, userId, deviceUuid, uploadFinishOptions)\n                val broadcastResponse = directMessagesRepository.broadcastVoice(\n                    csrfToken,\n                    viewerId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    uploadDmVoiceOptions.uploadId,\n                    waveform,\n                    samplingFreq\n                )\n                parseResponse(broadcastResponse, data, directItem)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, directItem))\n                Log.e(TAG, \"sendVoice: \", e)\n            }\n        }\n    }\n\n    fun sendReaction(\n        item: DirectItem,\n        emoji: Emoji,\n        scope: CoroutineScope,\n    ): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val userId = getCurrentUserId(data)\n        if (userId == null) {\n            data.postValue(error(\"userId is null\", null))\n            return data\n        }\n        val clientContext = UUID.randomUUID().toString()\n        // Log.d(TAG, \"sendText: sending: itemId: \" + directItem.getItemId());\n        data.postValue(loading(item))\n        addReaction(item, emoji)\n        var emojiUnicode: String? = null\n        if (emoji.unicode != \"❤️\") {\n            emojiUnicode = emoji.unicode\n        }\n        val itemId = item.itemId\n        if (itemId == null) {\n            data.postValue(error(\"itemId is null\", null))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.broadcastReaction(\n                    csrfToken,\n                    userId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    itemId,\n                    emojiUnicode,\n                    false\n                )\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n                Log.e(TAG, \"sendReaction: \", e)\n            }\n        }\n        return data\n    }\n\n    fun sendDeleteReaction(itemId: String, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val item = getItem(itemId)\n        if (item == null) {\n            data.postValue(error(\"Invalid item\", null))\n            return data\n        }\n        val reactions = item.reactions\n        if (reactions == null) {\n            // already removed?\n            data.postValue(success(item))\n            return data\n        }\n        removeReaction(item)\n        val clientContext = UUID.randomUUID().toString()\n        val itemId1 = item.itemId\n        if (itemId1 == null) {\n            data.postValue(error(\"itemId is null\", null))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.broadcastReaction(\n                    csrfToken,\n                    viewerId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    itemId1,\n                    null,\n                    true\n                )\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n                Log.e(TAG, \"sendDeleteReaction: \", e)\n            }\n        }\n        return data\n    }\n\n    fun unsend(item: DirectItem, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val index = removeItem(item)\n        val itemId = item.itemId\n        if (itemId == null) {\n            data.postValue(error(\"itemId is null\", null))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.deleteItem(csrfToken, deviceUuid, threadId, itemId)\n            } catch (e: Exception) {\n                // add the item back if unsuccessful\n                addItems(index, listOf(item))\n                data.postValue(error(e.message, item))\n                Log.e(TAG, \"unsend: \", e)\n            }\n        }\n        return data\n    }\n\n    fun forward(\n        recipients: Set<RankedRecipient>,\n        itemToForward: DirectItem,\n        scope: CoroutineScope,\n    ) {\n        for (recipient in recipients) {\n            forward(recipient, itemToForward, scope)\n        }\n    }\n\n    fun forward(\n        recipient: RankedRecipient,\n        itemToForward: DirectItem,\n        scope: CoroutineScope,\n    ) {\n        if (recipient.thread == null && recipient.user != null) {\n            scope.launch(Dispatchers.IO) {\n                // create thread and forward\n                val thread = DirectMessagesManager.createThread(recipient.user.pk)\n                forward(thread, itemToForward, scope)\n            }\n            return\n        }\n        if (recipient.thread != null) {\n            // just forward\n            val thread = recipient.thread\n            forward(thread, itemToForward, scope)\n        }\n    }\n\n    fun setReplyToItem(item: DirectItem?) {\n        // Log.d(TAG, \"setReplyToItem: \" + item);\n        _replyToItem.postValue(item)\n    }\n\n    private fun forward(\n        thread: DirectThread,\n        itemToForward: DirectItem,\n        scope: CoroutineScope,\n    ): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        val forwardItemId = itemToForward.itemId\n        if (forwardItemId == null) {\n            data.postValue(error(\"item id is null\", null))\n            return data\n        }\n        val itemType = itemToForward.itemType\n        if (itemType == null) {\n            data.postValue(error(\"item type is null\", null))\n            return data\n        }\n        val itemTypeName = DirectItemType.getName(itemType)\n        if (itemTypeName == null) {\n            Log.e(TAG, \"forward: itemTypeName was null!\")\n            data.postValue(error(\"itemTypeName is null\", null))\n            return data\n        }\n        data.postValue(loading(null))\n        if (thread.threadId == null) {\n            Log.e(TAG, \"forward: threadId was null!\")\n            data.postValue(error(\"threadId is null\", null))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.forward(\n                    thread.threadId,\n                    itemTypeName,\n                    threadId,\n                    forwardItemId\n                )\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"forward: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun acceptRequest(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.approveRequest(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"acceptRequest: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun declineRequest(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.declineRequest(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"declineRequest: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun refreshChats(scope: CoroutineScope) {\n        val isFetching = _fetching.value\n        if (isFetching != null && isFetching.status === Resource.Status.LOADING) {\n            stopCurrentRequest()\n        }\n        cursor = null\n        hasOlder = true\n        fetchChats(scope)\n    }\n\n    private fun sendPhoto(\n        data: MutableLiveData<Resource<Any?>>,\n        uri: Uri,\n        scope: CoroutineScope,\n    ) {\n        try {\n            val dimensions = BitmapUtils.decodeDimensions(contentResolver, uri)\n            if (dimensions == null) {\n                data.postValue(error(\"Decoding dimensions failed\", null))\n                return\n            }\n            sendPhoto(data, uri, dimensions.first, dimensions.second, scope)\n        } catch (e: IOException) {\n            data.postValue(error(e.message, null))\n            Log.e(TAG, \"sendPhoto: \", e)\n        }\n    }\n\n    private fun sendPhoto(\n        data: MutableLiveData<Resource<Any?>>,\n        uri: Uri,\n        width: Int,\n        height: Int,\n        scope: CoroutineScope,\n    ) {\n        val clientContext = UUID.randomUUID().toString()\n        val directItem = createImageOrVideo(viewerId, clientContext, uri, width, height, false)\n        directItem.isPending = true\n        addItems(0, listOf(directItem))\n        data.postValue(loading(directItem))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = uploadPhoto(uri, contentResolver)\n                if (handleInvalidResponse(data, response)) return@launch\n                val response1 = response.response ?: return@launch\n                val uploadId = response1.optString(\"upload_id\")\n                val response2 = directMessagesRepository.broadcastPhoto(csrfToken, viewerId, deviceUuid, clientContext, threadIdsOrUserIds, uploadId)\n                parseResponse(response2, data, directItem)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n                Log.e(TAG, \"sendPhoto: \", e)\n            }\n        }\n    }\n\n    private fun sendVideo(\n        data: MutableLiveData<Resource<Any?>>,\n        uri: Uri,\n        scope: CoroutineScope,\n    ) {\n        MediaUtils.getVideoInfo(contentResolver, uri, object : OnInfoLoadListener<VideoInfo?> {\n            override fun onLoad(info: VideoInfo?) {\n                if (info == null) {\n                    data.postValue(error(\"Could not get the video info\", null))\n                    return\n                }\n                sendVideo(data, uri, info.size, info.duration, info.width, info.height, scope)\n            }\n\n            override fun onFailure(t: Throwable) {\n                data.postValue(error(t.message, null))\n            }\n        })\n    }\n\n    private fun sendVideo(\n        data: MutableLiveData<Resource<Any?>>,\n        uri: Uri,\n        byteLength: Long,\n        duration: Long,\n        width: Int,\n        height: Int,\n        scope: CoroutineScope,\n    ) {\n        if (duration > 60000) {\n            // instagram does not allow uploading videos longer than 60 secs for Direct messages\n            data.postValue(error(R.string.dms_ERROR_VIDEO_TOO_LONG, null))\n            return\n        }\n        val userId = getCurrentUserId(data) ?: return\n        val clientContext = UUID.randomUUID().toString()\n        val directItem = createImageOrVideo(userId, clientContext, uri, width, height, true)\n        directItem.isPending = true\n        addItems(0, listOf(directItem))\n        data.postValue(loading(directItem))\n        val uploadDmVideoOptions = createUploadDmVideoOptions(byteLength, duration, width, height)\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = uploadVideo(uri, contentResolver, uploadDmVideoOptions)\n                // Log.d(TAG, \"onUploadComplete: \" + response);\n                if (handleInvalidResponse(data, response)) return@launch\n                val uploadFinishOptions = UploadFinishOptions(\n                    uploadDmVideoOptions.uploadId,\n                    \"2\",\n                    VideoOptions(duration / 1000f, emptyList(), 0, false)\n                )\n                mediaRepository.uploadFinish(csrfToken, userId, deviceUuid, uploadFinishOptions)\n                val broadcastResponse = directMessagesRepository.broadcastVideo(\n                    csrfToken,\n                    viewerId,\n                    deviceUuid,\n                    clientContext,\n                    threadIdsOrUserIds,\n                    uploadDmVideoOptions.uploadId,\n                    \"\",\n                    true\n                )\n                parseResponse(broadcastResponse, data, directItem)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, directItem))\n                Log.e(TAG, \"sendVideo: \", e)\n            }\n        }\n    }\n\n    private fun parseResponse(\n        response: DirectThreadBroadcastResponse,\n        data: MutableLiveData<Resource<Any?>>,\n        directItem: DirectItem,\n    ) {\n        val payloadClientContext: String?\n        val timestamp: Long\n        val itemId: String?\n        val payload = response.payload\n        if (payload == null) {\n            val messageMetadata = response.messageMetadata\n            if (messageMetadata == null || messageMetadata.isEmpty()) {\n                data.postValue(success(directItem))\n                return\n            }\n            val (clientContext, itemId1, timestamp1) = messageMetadata[0]\n            payloadClientContext = clientContext\n            itemId = itemId1\n            timestamp = timestamp1\n        } else {\n            payloadClientContext = payload.clientContext\n            timestamp = payload.timestamp\n            itemId = payload.itemId\n        }\n        if (payloadClientContext == null) {\n            data.postValue(error(\"clientContext in response was null\", null))\n            return\n        }\n        updateItemSent(payloadClientContext, timestamp, itemId)\n        data.postValue(success(directItem))\n    }\n\n    private fun updateItemSent(\n        clientContext: String,\n        timestamp: Long,\n        itemId: String?,\n    ) {\n        val items = items.value\n        val list = if (items == null) LinkedList() else LinkedList(items)\n        val index = list.indexOfFirst { it?.clientContext == clientContext }\n        if (index < 0) return\n        val directItem = list[index]\n        try {\n            val itemClone = directItem.clone() as DirectItem\n            itemClone.itemId = itemId\n            itemClone.isPending = false\n            itemClone.setTimestamp(timestamp)\n            list[index] = itemClone\n            inboxManager.setItemsToThread(threadId, list)\n        } catch (e: CloneNotSupportedException) {\n            Log.e(TAG, \"updateItemSent: \", e)\n        }\n    }\n\n    private fun handleInvalidResponse(\n        data: MutableLiveData<Resource<Any?>>,\n        response: MediaUploadResponse,\n    ): Boolean {\n        val responseJson = response.response\n        if (responseJson == null || response.responseCode != HttpURLConnection.HTTP_OK) {\n            data.postValue(error(R.string.generic_not_ok_response, null))\n            return true\n        }\n        val status = responseJson.optString(\"status\")\n        if (isEmpty(status) || status != \"ok\") {\n            data.postValue(error(R.string.generic_not_ok_response, null))\n            return true\n        }\n        return false\n    }\n\n    private fun getItemIndex(item: DirectItem, list: List<DirectItem?>): Int {\n        return Iterables.indexOf(list) { i: DirectItem? -> i != null && i.itemId == item.itemId }\n    }\n\n    private fun getItem(itemId: String): DirectItem? {\n        val items = items.value ?: return null\n        return items.asSequence()\n            .filter { it.itemId == itemId }\n            .firstOrNull()\n    }\n\n    private fun stopCurrentRequest() {\n        chatsRequest?.let {\n            if (it.isExecuted || it.isCanceled) return\n            it.cancel()\n        }\n        _fetching.postValue(success(Any()))\n    }\n\n    private fun getCurrentUserId(data: MutableLiveData<Resource<Any?>>): Long? {\n        if (currentUser == null || currentUser.pk <= 0) {\n            data.postValue(error(R.string.dms_ERROR_INVALID_USER, null))\n            return null\n        }\n        return currentUser.pk\n    }\n\n    fun removeThread() {\n        val pendingValue = isPending.value\n        val threadInPending = pendingValue != null && pendingValue\n        inboxManager.removeThread(threadId)\n        if (threadInPending) {\n            val totalValue = inboxManager.getPendingRequestsTotal().value ?: return\n            inboxManager.setPendingRequestsTotal(totalValue - 1)\n        }\n    }\n\n    fun updateTitle(newTitle: String, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.updateTitle(csrfToken, deviceUuid, threadId, newTitle.trim())\n                handleDetailsChangeResponse(data, response)\n            } catch (e: Exception) {\n            }\n        }\n        return data\n    }\n\n    fun addMembers(users: Set<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.addUsers(\n                    csrfToken,\n                    deviceUuid,\n                    threadId,\n                    users.map { obj: User -> obj.pk }\n                )\n                handleDetailsChangeResponse(data, response)\n            } catch (e: Exception) {\n                Log.e(TAG, \"addMembers: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun removeMember(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.removeUsers(csrfToken, deviceUuid, threadId, setOf(user.pk))\n                data.postValue(success(Any()))\n                var activeUsers = users.value\n                var leftUsersValue = leftUsers.value\n                if (activeUsers == null) {\n                    activeUsers = emptyList()\n                }\n                if (leftUsersValue == null) {\n                    leftUsersValue = emptyList()\n                }\n                val updatedActiveUsers = activeUsers.filter { u: User -> u.pk != user.pk }\n                val updatedLeftUsersBuilder = ImmutableList.builder<User>().addAll(leftUsersValue)\n                if (!leftUsersValue.contains(user)) {\n                    updatedLeftUsersBuilder.add(user)\n                }\n                val updatedLeftUsers = updatedLeftUsersBuilder.build()\n                setThreadUsers(updatedActiveUsers, updatedLeftUsers)\n            } catch (e: Exception) {\n                Log.e(TAG, \"removeMember: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun isAdmin(user: User): Boolean {\n        val adminUserIdsValue = adminUserIds.value\n        return adminUserIdsValue != null && adminUserIdsValue.contains(user.pk)\n    }\n\n    fun makeAdmin(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        if (isAdmin(user)) return data\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.addAdmins(csrfToken, deviceUuid, threadId, setOf(user.pk))\n                val currentAdminIds = adminUserIds.value\n                val updatedAdminIds = ImmutableList.builder<Long>()\n                    .addAll(currentAdminIds ?: emptyList())\n                    .add(user.pk)\n                    .build()\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.adminUserIds = updatedAdminIds\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"makeAdmin: \", e)\n                }\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"makeAdmin: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun removeAdmin(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        if (!isAdmin(user)) return data\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.removeAdmins(csrfToken, deviceUuid, threadId, setOf(user.pk))\n                val currentAdmins = adminUserIds.value ?: return@launch\n                val updatedAdminUserIds = currentAdmins.filter { userId1: Long -> userId1 != user.pk }\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.adminUserIds = updatedAdminUserIds\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"removeAdmin: \", e)\n                }\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"removeAdmin: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun mute(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val muted = isMuted.value\n        if (muted != null && muted) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.mute(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.muted = true\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"mute: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"mute: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unmute(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val muted = isMuted.value\n        if (muted != null && !muted) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.unmute(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.muted = false\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"unmute: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"unmute: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun muteMentions(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val mentionsMuted = isMentionsMuted.value\n        if (mentionsMuted != null && mentionsMuted) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.muteMentions(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.mentionsMuted = true\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"muteMentions: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"muteMentions: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unmuteMentions(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val mentionsMuted = isMentionsMuted.value\n        if (mentionsMuted != null && !mentionsMuted) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                directMessagesRepository.unmuteMentions(csrfToken, deviceUuid, threadId)\n                data.postValue(success(Any()))\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.mentionsMuted = false\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"unmuteMentions: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"unmuteMentions: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun blockUser(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                friendshipRepository.changeBlock(csrfToken, viewerId, deviceUuid, false, user.pk)\n                refreshChats(scope)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onFailure: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unblockUser(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                friendshipRepository.changeBlock(csrfToken, viewerId, deviceUuid, true, user.pk)\n                refreshChats(scope)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onFailure: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun restrictUser(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                friendshipRepository.toggleRestrict(csrfToken, deviceUuid, user.pk, true)\n                refreshChats(scope)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onFailure: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unRestrictUser(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        scope.launch(Dispatchers.IO) {\n            try {\n                friendshipRepository.toggleRestrict(csrfToken, deviceUuid, user.pk, false)\n                refreshChats(scope)\n            } catch (e: Exception) {\n                Log.e(TAG, \"onFailure: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun approveUsers(users: List<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.approveParticipantRequests(\n                    csrfToken,\n                    deviceUuid,\n                    threadId,\n                    users.map { obj: User -> obj.pk }\n                )\n                handleDetailsChangeResponse(data, response)\n                pendingUserApproveDenySuccessAction(users)\n            } catch (e: Exception) {\n                Log.e(TAG, \"approveUsers: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun denyUsers(users: List<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.declineParticipantRequests(\n                    csrfToken,\n                    deviceUuid,\n                    threadId,\n                    users.map { obj: User -> obj.pk }\n                )\n                handleDetailsChangeResponse(data, response)\n                pendingUserApproveDenySuccessAction(users)\n            } catch (e: Exception) {\n                Log.e(TAG, \"denyUsers: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    private fun pendingUserApproveDenySuccessAction(users: List<User>) {\n        val pendingRequestsValue = _pendingRequests.value ?: return\n        val pendingUsers = pendingRequestsValue.users\n        if (pendingUsers == null || pendingUsers.isEmpty()) return\n        val filtered = pendingUsers.filter { o: User -> !users.contains(o) }\n        try {\n            val clone = pendingRequestsValue.clone() as DirectThreadParticipantRequestsResponse\n            clone.users = filtered\n            val totalParticipantRequests = clone.totalParticipantRequests\n            clone.totalParticipantRequests = if (totalParticipantRequests > 0) totalParticipantRequests - 1 else 0\n            _pendingRequests.postValue(clone)\n        } catch (e: CloneNotSupportedException) {\n            Log.e(TAG, \"pendingUserApproveDenySuccessAction: \", e)\n        }\n    }\n\n    fun approvalRequired(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val approvalRequiredToJoin = isApprovalRequiredToJoin.value\n        if (approvalRequiredToJoin != null && approvalRequiredToJoin) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.approvalRequired(csrfToken, deviceUuid, threadId)\n                handleDetailsChangeResponse(data, response)\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.approvalRequiredForNewMembers = true\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"onResponse: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"approvalRequired: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun approvalNotRequired(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        val approvalRequiredToJoin = isApprovalRequiredToJoin.value\n        if (approvalRequiredToJoin != null && !approvalRequiredToJoin) {\n            data.postValue(success(Any()))\n            return data\n        }\n        scope.launch(Dispatchers.IO) {\n            try {\n                val request = directMessagesRepository.approvalNotRequired(csrfToken, deviceUuid, threadId)\n                handleDetailsChangeResponse(data, request)\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.approvalRequiredForNewMembers = false\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"onResponse: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"approvalNotRequired: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun leave(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val request = directMessagesRepository.leave(csrfToken, deviceUuid, threadId)\n                handleDetailsChangeResponse(data, request)\n            } catch (e: Exception) {\n                Log.e(TAG, \"leave: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun end(scope: CoroutineScope): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val request = directMessagesRepository.end(csrfToken, deviceUuid, threadId)\n                handleDetailsChangeResponse(data, request)\n                val currentThread = thread.value ?: return@launch\n                try {\n                    val thread = currentThread.clone() as DirectThread\n                    thread.inputMode = 1\n                    inboxManager.setThread(threadId, thread)\n                } catch (e: CloneNotSupportedException) {\n                    Log.e(TAG, \"onResponse: \", e)\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"leave: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    private fun handleDetailsChangeResponse(\n        data: MutableLiveData<Resource<Any?>>,\n        response: DirectThreadDetailsChangeResponse,\n    ) {\n        data.postValue(success(Any()))\n        val thread = response.thread\n        if (thread != null) {\n            setThread(thread, true)\n        }\n    }\n\n    fun markAsSeen(\n        directItem: DirectItem,\n        scope: CoroutineScope,\n    ): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        scope.launch(Dispatchers.IO) {\n            try {\n                val response = directMessagesRepository.markAsSeen(csrfToken, deviceUuid, threadId, directItem)\n                if (response == null) {\n                    data.postValue(error(R.string.generic_null_response, null))\n                    return@launch\n                }\n                if (currentUser == null) return@launch\n                inboxManager.fetchUnseenCount(scope)\n                val payload = response.payload ?: return@launch\n                val timestamp = payload.timestamp\n                val thread = thread.value ?: return@launch\n                val currentLastSeenAt = thread.lastSeenAt\n                val lastSeenAt = if (currentLastSeenAt == null) HashMap() else HashMap(currentLastSeenAt)\n                lastSeenAt[currentUser.pk] = DirectThreadLastSeenAt(timestamp, directItem.itemId)\n                thread.lastSeenAt = lastSeenAt\n                setThread(thread, true)\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"markAsSeen: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/Comment.kt",
    "content": "package awais.instagrabber.models\n\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.utils.TextUtils\nimport java.io.Serializable\nimport java.util.*\n\nclass Comment(\n    val pk: String,\n    val text: String,\n    val createdAt: Long,\n    var commentLikeCount: Long,\n    private var hasLikedComment: Boolean,\n    val user: User,\n    val childCommentCount: Int\n) : Serializable, Cloneable {\n    val dateTime: String\n        get() = TextUtils.epochSecondToString(createdAt)\n\n    fun getLiked(): Boolean {\n        return hasLikedComment\n    }\n\n    fun setLiked(hasLikedComment: Boolean) {\n        commentLikeCount = if (hasLikedComment) commentLikeCount + 1 else commentLikeCount - 1\n        this.hasLikedComment = hasLikedComment\n    }\n\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any {\n        return super.clone()\n    }\n\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n\n        other as Comment\n\n        if (pk != other.pk) return false\n        if (text != other.text) return false\n        if (createdAt != other.createdAt) return false\n        if (commentLikeCount != other.commentLikeCount) return false\n        if (hasLikedComment != other.hasLikedComment) return false\n        if (user != other.user) return false\n        if (childCommentCount != other.childCommentCount) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = pk.hashCode()\n        result = 31 * result + text.hashCode()\n        result = 31 * result + createdAt.hashCode()\n        result = 31 * result + commentLikeCount.hashCode()\n        result = 31 * result + hasLikedComment.hashCode()\n        result = 31 * result + user.hashCode()\n        result = 31 * result + childCommentCount\n        return result\n    }\n\n    override fun toString(): String {\n        return \"Comment(pk='$pk', text='$text', createdAt=$createdAt, commentLikeCount=$commentLikeCount, hasLikedComment=$hasLikedComment, user=$user, childCommentCount=$childCommentCount)\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/IntentModel.kt",
    "content": "package awais.instagrabber.models\n\nimport awais.instagrabber.models.enums.IntentModelType\n\ndata class IntentModel(val type: IntentModelType, val text: String)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/PostsLayoutPreferences.java",
    "content": "package awais.instagrabber.models;\n\nimport com.google.gson.Gson;\n\nimport java.util.Objects;\n\npublic final class PostsLayoutPreferences {\n    private final PostsLayoutType type;\n    private final int colCount;\n    private final boolean isAvatarVisible;\n    private final boolean isNameVisible;\n    private final ProfilePicSize profilePicSize;\n    private final boolean hasRoundedCorners;\n    private final boolean hasGap;\n    private final boolean animationDisabled;\n\n    public static class Builder {\n        private PostsLayoutType type = PostsLayoutType.GRID;\n        private int colCount = 3;\n        private boolean isAvatarVisible = true;\n        private boolean isNameVisible = false;\n        private ProfilePicSize profilePicSize = ProfilePicSize.SMALL;\n        private boolean hasRoundedCorners = true;\n        private boolean hasGap = true;\n        private boolean animationDisabled = false;\n\n        public Builder setType(final PostsLayoutType type) {\n            this.type = type;\n            return this;\n        }\n\n        public Builder setColCount(final int colCount) {\n            this.colCount = (colCount <= 0 || colCount > 3) ? 1 : colCount;\n            return this;\n        }\n\n        public Builder setAvatarVisible(final boolean avatarVisible) {\n            this.isAvatarVisible = avatarVisible;\n            return this;\n        }\n\n        public Builder setNameVisible(final boolean nameVisible) {\n            this.isNameVisible = nameVisible;\n            return this;\n        }\n\n        public Builder setProfilePicSize(final ProfilePicSize profilePicSize) {\n            this.profilePicSize = profilePicSize;\n            return this;\n        }\n\n        public Builder setHasRoundedCorners(final boolean hasRoundedCorners) {\n            this.hasRoundedCorners = hasRoundedCorners;\n            return this;\n        }\n\n        public Builder setHasGap(final boolean hasGap) {\n            this.hasGap = hasGap;\n            return this;\n        }\n\n        public Builder setAnimationDisabled(final boolean animationDisabled) {\n            this.animationDisabled = animationDisabled;\n            return this;\n        }\n\n        // Breaking builder pattern and adding getters to avoid too many object creations in PostsLayoutPreferencesDialogFragment\n        public PostsLayoutType getType() {\n            return type;\n        }\n\n        public int getColCount() {\n            return colCount;\n        }\n\n        public boolean isAvatarVisible() {\n            return isAvatarVisible;\n        }\n\n        public boolean isNameVisible() {\n            return isNameVisible;\n        }\n\n        public ProfilePicSize getProfilePicSize() {\n            return profilePicSize;\n        }\n\n        public boolean getHasRoundedCorners() {\n            return hasRoundedCorners;\n        }\n\n        public boolean getHasGap() {\n            return hasGap;\n        }\n\n        public boolean isAnimationDisabled() {\n            return animationDisabled;\n        }\n\n        public Builder mergeFrom(final PostsLayoutPreferences preferences) {\n            if (preferences == null) {\n                return this;\n            }\n            setColCount(preferences.getColCount());\n            setAvatarVisible(preferences.isAvatarVisible());\n            setNameVisible(preferences.isNameVisible());\n            setType(preferences.getType());\n            setProfilePicSize(preferences.getProfilePicSize());\n            setHasRoundedCorners(preferences.getHasRoundedCorners());\n            setHasGap(preferences.getHasGap());\n            setAnimationDisabled(preferences.isAnimationDisabled());\n            return this;\n        }\n\n        public PostsLayoutPreferences build() {\n            return new PostsLayoutPreferences(type, colCount, isAvatarVisible, isNameVisible, profilePicSize, hasRoundedCorners, hasGap,\n                                              animationDisabled);\n        }\n    }\n\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    private PostsLayoutPreferences(final PostsLayoutType type,\n                                   final int colCount,\n                                   final boolean isAvatarVisible,\n                                   final boolean isNameVisible,\n                                   final ProfilePicSize profilePicSize,\n                                   final boolean hasRoundedCorners,\n                                   final boolean hasGap,\n                                   final boolean animationDisabled) {\n\n        this.type = type;\n        this.colCount = colCount;\n        this.isAvatarVisible = isAvatarVisible;\n        this.isNameVisible = isNameVisible;\n        this.profilePicSize = profilePicSize;\n        this.hasRoundedCorners = hasRoundedCorners;\n        this.hasGap = hasGap;\n        this.animationDisabled = animationDisabled;\n    }\n\n    public PostsLayoutType getType() {\n        return type;\n    }\n\n    public int getColCount() {\n        return colCount;\n    }\n\n    public boolean isAvatarVisible() {\n        return isAvatarVisible;\n    }\n\n    public boolean isNameVisible() {\n        return isNameVisible;\n    }\n\n    public ProfilePicSize getProfilePicSize() {\n        return profilePicSize;\n    }\n\n    public boolean getHasRoundedCorners() {\n        return hasRoundedCorners;\n    }\n\n    public boolean getHasGap() {\n        return hasGap;\n    }\n\n    public String getJson() {\n        return new Gson().toJson(this);\n    }\n\n    public static PostsLayoutPreferences fromJson(final String json) {\n        if (json == null) return null;\n        return new Gson().fromJson(json, PostsLayoutPreferences.class);\n    }\n\n    public boolean isAnimationDisabled() {\n        return animationDisabled;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final PostsLayoutPreferences that = (PostsLayoutPreferences) o;\n        return colCount == that.colCount &&\n                isAvatarVisible == that.isAvatarVisible &&\n                isNameVisible == that.isNameVisible &&\n                type == that.type &&\n                profilePicSize == that.profilePicSize &&\n                animationDisabled == that.animationDisabled;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(type, colCount, isAvatarVisible, isNameVisible, profilePicSize, animationDisabled);\n    }\n\n    @Override\n    public String toString() {\n        return \"PostsLayoutPreferences{\" +\n                \"type=\" + type +\n                \", colCount=\" + colCount +\n                \", isAvatarVisible=\" + isAvatarVisible +\n                \", isNameVisible=\" + isNameVisible +\n                \", profilePicSize=\" + profilePicSize +\n                \", hasRoundedCorners=\" + hasRoundedCorners +\n                \", hasGap=\" + hasGap +\n                \", animationDisabled=\" + animationDisabled +\n                '}';\n    }\n\n    public enum PostsLayoutType {\n        GRID,\n        STAGGERED_GRID,\n        LINEAR\n    }\n\n    public enum ProfilePicSize {\n        REGULAR,\n        SMALL,\n        TINY\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/Resource.kt",
    "content": "package awais.instagrabber.models\n\nimport androidx.annotation.StringRes\n\ndata class Resource<T>(\n    @JvmField val status: Status,\n    @JvmField val data: T? = null,\n    @JvmField val message: String? = null,\n    @JvmField @StringRes val resId: Int = 0,\n) {\n    enum class Status {\n        SUCCESS, ERROR, LOADING\n    }\n\n    companion object {\n        @JvmStatic\n        fun <T> success(data: T): Resource<T> {\n            return Resource(Status.SUCCESS, data, null, 0)\n        }\n\n        @JvmStatic\n        fun <T> error(msg: String?, data: T?): Resource<T?> {\n            return Resource(Status.ERROR, data, msg, 0)\n        }\n\n        @JvmStatic\n        fun <T> error(resId: Int, data: T?): Resource<T?> {\n            return Resource(Status.ERROR, data, null, resId)\n        }\n\n        @JvmStatic\n        fun <T> loading(data: T?): Resource<T?> {\n            return Resource(Status.LOADING, data, null, 0)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/SavedImageEditState.kt",
    "content": "package awais.instagrabber.models\n\nimport android.graphics.RectF\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper\nimport awais.instagrabber.utils.SerializablePair\nimport java.util.*\n\ndata class SavedImageEditState(val sessionId: String, val originalPath: String) {\n    var cropImageMatrixValues: FloatArray? = null // 9 values of matrix\n    var cropRect: RectF? = null\n    var appliedTuningFilters: HashMap<FiltersHelper.FilterType, Map<Int, Any>>? = null\n    var appliedFilter: SerializablePair<FiltersHelper.FilterType, Map<Int, Any>>? = null\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/Tab.kt",
    "content": "package awais.instagrabber.models\n\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.IdRes\nimport androidx.annotation.NavigationRes\n\ndata class Tab(\n    @param:DrawableRes val iconResId: Int,\n    val title: String,\n    val isRemovable: Boolean,\n\n    /**\n     * This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)\n     */\n    @param:NavigationRes val navigationResId: Int,\n\n    /**\n     * This is the resource id of the root navigation tag of the navigation resource.\n     *\n     * eg: inside R.navigation.direct_messages_nav_graph, the id of the root tag is R.id.direct_messages_nav_graph.\n     *\n     * So this field would equal to the value of R.id.direct_messages_nav_graph\n     */\n    @param:IdRes val navigationRootId: Int,\n\n    /**\n     * This is the start destination of the nav graph\n     */\n    @param:IdRes val startDestinationFragmentId: Int,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/UploadPhotoOptions.kt",
    "content": "package awais.instagrabber.models\n\ndata class UploadPhotoOptions(\n    val uploadId: String? = null,\n    val name: String,\n    val byteLength: Long = 0,\n    val isSideCar: Boolean = false,\n    val waterfallId: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/UploadVideoOptions.kt",
    "content": "package awais.instagrabber.models\n\nimport awais.instagrabber.models.enums.MediaItemType\n\ndata class UploadVideoOptions(\n    val uploadId: String,\n    val name: String,\n    val byteLength: Long = 0,\n    val duration: Long = 0,\n    val width: Int = 0,\n    val height: Int = 0,\n    val isSideCar: Boolean = false,\n    // Stories\n    val forAlbum: Boolean = false,\n    val isDirect: Boolean = false,\n    val isDirectVoice: Boolean = false,\n    val isForDirectStory: Boolean = false,\n    val isIgtvVideo: Boolean = false,\n    val waterfallId: String? = null,\n    val offset: Long = 0,\n    val mediaType: MediaItemType? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/BroadcastItemType.kt",
    "content": "package awais.instagrabber.models.enums\n\nenum class BroadcastItemType(val value: String) {\n    TEXT(\"text\"),\n    REACTION(\"reaction\"),\n    REELSHARE(\"reel_share\"),\n    IMAGE(\"configure_photo\"),\n    LINK(\"link\"),\n    VIDEO(\"configure_video\"),\n    VOICE(\"share_voice\"),\n    ANIMATED_MEDIA(\"animated_media\"),\n    MEDIA_SHARE(\"media_share\"),\n    PROFILE(\"profile\"),\n    STORY(\"story_share\"), // not reply\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/DirectItemType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport com.google.gson.annotations.SerializedName\nimport java.io.Serializable\n\nenum class DirectItemType(val id: Int) : Serializable {\n    UNKNOWN(0),\n\n    @SerializedName(\"text\")\n    TEXT(1),\n\n    @SerializedName(\"like\")\n    LIKE(2),\n\n    @SerializedName(\"link\")\n    LINK(3),\n\n    @SerializedName(\"media\")\n    MEDIA(4),\n\n    @SerializedName(\"raven_media\")\n    RAVEN_MEDIA(5),\n\n    @SerializedName(\"profile\")\n    PROFILE(6),\n\n    @SerializedName(\"video_call_event\")\n    VIDEO_CALL_EVENT(7),\n\n    @SerializedName(\"animated_media\")\n    ANIMATED_MEDIA(8),\n\n    @SerializedName(\"voice_media\")\n    VOICE_MEDIA(9),\n\n    @SerializedName(\"media_share\")\n    MEDIA_SHARE(10),\n\n    @SerializedName(\"reel_share\")\n    REEL_SHARE(11),\n\n    @SerializedName(\"action_log\")\n    ACTION_LOG(12),\n\n    @SerializedName(\"placeholder\")\n    PLACEHOLDER(13),\n\n    @SerializedName(\"story_share\")\n    STORY_SHARE(14),\n\n    @SerializedName(\"clip\")\n    CLIP(15),        // media_share but reel\n\n    @SerializedName(\"felix_share\")\n    FELIX_SHARE(16), // media_share but igtv\n\n    @SerializedName(\"location\")\n    LOCATION(17),\n\n    @SerializedName(\"xma\")\n    XMA(18); // self avatar stickers\n\n    companion object {\n        private val map: MutableMap<Int, DirectItemType> = mutableMapOf()\n\n        @JvmStatic\n        fun getTypeFromId(id: Int): DirectItemType {\n            return map[id] ?: UNKNOWN\n        }\n\n        fun getName(directItemType: DirectItemType): String? {\n            when (directItemType) {\n                TEXT -> return \"text\"\n                LIKE -> return \"like\"\n                LINK -> return \"link\"\n                MEDIA -> return \"media\"\n                RAVEN_MEDIA -> return \"raven_media\"\n                PROFILE -> return \"profile\"\n                VIDEO_CALL_EVENT -> return \"video_call_event\"\n                ANIMATED_MEDIA -> return \"animated_media\"\n                VOICE_MEDIA -> return \"voice_media\"\n                MEDIA_SHARE -> return \"media_share\"\n                REEL_SHARE -> return \"reel_share\"\n                ACTION_LOG -> return \"action_log\"\n                PLACEHOLDER -> return \"placeholder\"\n                STORY_SHARE -> return \"story_share\"\n                CLIP -> return \"clip\"\n                FELIX_SHARE -> return \"felix_share\"\n                LOCATION -> return \"location\"\n                else -> return null\n            }\n        }\n\n        init {\n            for (type in values()) {\n                map[type.id] = type\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/FavoriteType.kt",
    "content": "package awais.instagrabber.models.enums\n\nenum class FavoriteType {\n    TOP,  // used just for searching\n    USER,\n    HASHTAG,\n    LOCATION,\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/FollowingType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport java.io.Serializable\nimport java.util.*\n\nenum class FollowingType(val id: Int) : Serializable {\n    FOLLOWING(1),\n    NOT_FOLLOWING(0);\n\n    companion object {\n        private val map: MutableMap<Int, FollowingType> = mutableMapOf()\n\n        @JvmStatic\n        fun valueOf(id: Int): FollowingType? {\n            return map[id]\n        }\n\n        init {\n            for (type in values()) {\n                map[type.id] = type\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/IntentModelType.kt",
    "content": "package awais.instagrabber.models.enums\n\nenum class IntentModelType {\n    UNKNOWN,\n    USERNAME,\n    POST,\n    HASHTAG,\n    LOCATION,\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/MediaItemType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport java.io.Serializable\n\nenum class MediaItemType(val id: Int) : Serializable {\n    MEDIA_TYPE_IMAGE(1),\n    MEDIA_TYPE_VIDEO(2),\n    MEDIA_TYPE_SLIDER(8),\n    MEDIA_TYPE_VOICE(11),\n    MEDIA_TYPE_LIVE(5); // arbitrary\n\n    companion object {\n        private val map: MutableMap<Int, MediaItemType> = mutableMapOf()\n\n        @JvmStatic\n        fun valueOf(id: Int): MediaItemType? {\n            return map[id]\n        }\n\n        init {\n            for (type in values()) {\n                map[type.id] = type\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/NotificationType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport java.io.Serializable\n\nenum class NotificationType(val itemType: Int) : Serializable {\n    LIKE(60),\n    FOLLOW(101),\n    COMMENT(12),  // NOT TESTED\n    COMMENT_MENTION(66),\n    TAGGED(102),  // NOT TESTED\n    COMMENT_LIKE(13),\n    TAGGED_COMMENT(14),\n    RESPONDED_STORY(213),\n    REQUEST(75),\n    AYML(9999);\n\n    companion object {\n        private val map: MutableMap<Int, NotificationType> = mutableMapOf()\n\n        @JvmStatic\n        fun valueOfType(itemType: Int): NotificationType? {\n            return map[itemType]\n        }\n\n        init {\n            for (type in values()) {\n                map[type.itemType] = type\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/PostItemType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport java.io.Serializable\n\nenum class PostItemType : Serializable {\n    MAIN, DISCOVER, FEED, SAVED, COLLECTION, LIKED, TAGGED, HASHTAG, LOCATION\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/RavenMediaViewMode.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport com.google.gson.annotations.SerializedName\n\nenum class RavenMediaViewMode {\n    @SerializedName(\"permanent\")\n    PERMANENT,\n    @SerializedName(\"replayable\")\n    REPLAYABLE,\n    @SerializedName(\"once\")\n    ONCE,\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/models/enums/StoryPaginationType.kt",
    "content": "package awais.instagrabber.models.enums\n\nimport java.io.Serializable\n\nenum class StoryPaginationType : Serializable {\n    FORWARD, BACKWARD, DO_NOTHING, ERROR\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/CollectionRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.UserFeedResponse;\nimport awais.instagrabber.repositories.responses.WrappedFeedResponse;\nimport awais.instagrabber.repositories.responses.saved.CollectionsListResponse;\nimport retrofit2.Call;\nimport retrofit2.http.FieldMap;\nimport retrofit2.http.FormUrlEncoded;\nimport retrofit2.http.GET;\nimport retrofit2.http.POST;\nimport retrofit2.http.Path;\nimport retrofit2.http.QueryMap;\n\npublic interface CollectionRepository {\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/collections/{id}/{action}/\")\n    Call<String> changeCollection(@Path(\"id\") String id,\n                                  @Path(\"action\") String action,\n                                  @FieldMap Map<String, String> signedForm);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/CommentRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.CommentsFetchResponse;\nimport awais.instagrabber.repositories.responses.ChildCommentsFetchResponse;\nimport retrofit2.Call;\nimport retrofit2.http.FieldMap;\nimport retrofit2.http.FormUrlEncoded;\nimport retrofit2.http.GET;\nimport retrofit2.http.Header;\nimport retrofit2.http.POST;\nimport retrofit2.http.Path;\nimport retrofit2.http.Query;\nimport retrofit2.http.QueryMap;\n\npublic interface CommentRepository {\n    @GET(\"/api/v1/media/{mediaId}/comments/\")\n    Call<CommentsFetchResponse> fetchComments(@Path(\"mediaId\") final String mediaId,\n                                              @QueryMap final Map<String, String> queryMap);\n\n    @GET(\"/api/v1/media/{mediaId}/comments/{commentId}/inline_child_comments/\")\n    Call<ChildCommentsFetchResponse> fetchChildComments(@Path(\"mediaId\") final String mediaId,\n                                                        @Path(\"commentId\") final String commentId,\n                                                        @QueryMap final Map<String, String> queryMap);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/comment/\")\n    Call<String> comment(@Path(\"mediaId\") final String mediaId,\n                         @FieldMap final Map<String, String> signedForm);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/comment/bulk_delete/\")\n    Call<String> commentsBulkDelete(@Path(\"mediaId\") final String mediaId,\n                                    @FieldMap final Map<String, String> signedForm);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{commentId}/comment_like/\")\n    Call<String> commentLike(@Path(\"commentId\") final String commentId,\n                             @FieldMap final Map<String, String> signedForm);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{commentId}/comment_unlike/\")\n    Call<String> commentUnlike(@Path(\"commentId\") final String commentId,\n                               @FieldMap final Map<String, String> signedForm);\n\n    @GET(\"/api/v1/language/translate/\")\n    Call<String> translate(@QueryMap final Map<String, String> form);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/DirectMessagesService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.directmessages.*\nimport retrofit2.http.*\n\ninterface DirectMessagesService {\n    @GET(\"/api/v1/direct_v2/inbox/\")\n    suspend fun fetchInbox(@QueryMap queryMap: Map<String, String>): DirectInboxResponse\n\n    @GET(\"/api/v1/direct_v2/pending_inbox/\")\n    suspend fun fetchPendingInbox(@QueryMap queryMap: Map<String, String>): DirectInboxResponse\n\n    @GET(\"/api/v1/direct_v2/threads/{threadId}/\")\n    suspend fun fetchThread(\n        @Path(\"threadId\") threadId: String,\n        @QueryMap queryMap: Map<String, String>,\n    ): DirectThreadFeedResponse\n\n    @GET(\"/api/v1/direct_v2/get_badge_count/?no_raven=1\")\n    suspend fun fetchUnseenCount(): DirectBadgeCount\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/broadcast/{item}/\")\n    suspend fun broadcast(\n        @Path(\"item\") item: String,\n        @FieldMap signedForm: Map<String, String>,\n    ): DirectThreadBroadcastResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/add_user/\")\n    suspend fun addUsers(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/remove_users/\")\n    suspend fun removeUsers(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/update_title/\")\n    suspend fun updateTitle(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/add_admins/\")\n    suspend fun addAdmins(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/remove_admins/\")\n    suspend fun removeAdmins(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/items/{itemId}/delete/\")\n    suspend fun deleteItem(\n        @Path(\"threadId\") threadId: String,\n        @Path(\"itemId\") itemId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @GET(\"/api/v1/direct_v2/ranked_recipients/\")\n    suspend fun rankedRecipients(@QueryMap queryMap: Map<String, String>): RankedRecipientsResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/broadcast/forward/\")\n    suspend fun forward(@FieldMap form: Map<String, String>): DirectThreadBroadcastResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/create_group_thread/\")\n    suspend fun createThread(@FieldMap signedForm: Map<String, String>): DirectThread\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/mute/\")\n    suspend fun mute(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/unmute/\")\n    suspend fun unmute(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/mute_mentions/\")\n    suspend fun muteMentions(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String?>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/unmute_mentions/\")\n    suspend fun unmuteMentions(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @GET(\"/api/v1/direct_v2/threads/{threadId}/participant_requests/\")\n    suspend fun participantRequests(\n        @Path(\"threadId\") threadId: String,\n        @Query(\"page_size\") pageSize: Int,\n        @Query(\"cursor\") cursor: String?,\n    ): DirectThreadParticipantRequestsResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/approve_participant_requests/\")\n    suspend fun approveParticipantRequests(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/deny_participant_requests/\")\n    suspend fun declineParticipantRequests(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/approval_required_for_new_members/\")\n    suspend fun approvalRequired(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/approval_not_required_for_new_members/\")\n    suspend fun approvalNotRequired(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/leave/\")\n    suspend fun leave(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/remove_all_users/\")\n    suspend fun end(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectThreadDetailsChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/approve/\")\n    suspend fun approveRequest(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/decline/\")\n    suspend fun declineRequest(\n        @Path(\"threadId\") threadId: String,\n        @FieldMap form: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/direct_v2/threads/{threadId}/items/{itemId}/seen/\")\n    suspend fun markItemSeen(\n        @Path(\"threadId\") threadId: String,\n        @Path(\"itemId\") itemId: String,\n        @FieldMap form: Map<String, String>,\n    ): DirectItemSeenResponse\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/DiscoverRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;\nimport retrofit2.Call;\nimport retrofit2.http.GET;\nimport retrofit2.http.QueryMap;\n\npublic interface DiscoverRepository {\n    @GET(\"/api/v1/discover/topical_explore/\")\n    Call<TopicalExploreFeedResponse> topicalExplore(@QueryMap Map<String, String> queryParams);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/FeedRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.feed.FeedFetchResponse;\nimport retrofit2.Call;\nimport retrofit2.http.FieldMap;\nimport retrofit2.http.FormUrlEncoded;\nimport retrofit2.http.POST;\n\npublic interface FeedRepository {\n    @FormUrlEncoded\n    @POST(\"/api/v1/feed/timeline/\")\n    Call<FeedFetchResponse> fetch(@FieldMap final Map<String, String> signedForm);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/FriendshipService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.FriendshipChangeResponse\nimport awais.instagrabber.repositories.responses.FriendshipListFetchResponse\nimport awais.instagrabber.repositories.responses.FriendshipRestrictResponse\nimport retrofit2.http.*\n\ninterface FriendshipService {\n    @FormUrlEncoded\n    @POST(\"/api/v1/friendships/{action}/{id}/\")\n    suspend fun change(\n        @Path(\"action\") action: String,\n        @Path(\"id\") id: Long,\n        @FieldMap form: Map<String, String>,\n    ): FriendshipChangeResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/restrict_action/{action}/\")\n    suspend fun toggleRestrict(\n        @Path(\"action\") action: String,\n        @FieldMap form: Map<String, String>,\n    ): FriendshipRestrictResponse\n\n    @GET(\"/api/v1/friendships/{userId}/{type}/\")\n    suspend fun getList(\n        @Path(\"userId\") userId: Long,\n        @Path(\"type\") type: String,  // following or followers\n        @QueryMap(encoded = true) queryParams: Map<String, String>,\n    ): FriendshipListFetchResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/friendships/{action}/\")\n    suspend fun changeMute(\n        @Path(\"action\") action: String,\n        @FieldMap form: Map<String, String>,\n    ): FriendshipChangeResponse\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/GifRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;\nimport retrofit2.Call;\nimport retrofit2.http.GET;\nimport retrofit2.http.Query;\n\npublic interface GifRepository {\n\n    @GET(\"/api/v1/creatives/story_media_search_keyed_format/\")\n    Call<GiphyGifResponse> searchGiphyGifs(@Query(\"request_surface\") final String requestSurface,\n                                           @Query(\"q\") final String query,\n                                           @Query(\"media_types\") final String mediaTypes);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/GraphQLService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport retrofit2.http.GET\nimport retrofit2.http.Path\nimport retrofit2.http.QueryMap\n\ninterface GraphQLService {\n    @GET(\"/graphql/query/\")\n    suspend fun fetch(@QueryMap(encoded = true) queryParams: Map<String, String>): String\n\n    @GET(\"/{username}/\")\n    suspend fun getUser(@Path(\"username\") username: String): String\n\n    @GET(\"/p/{shortcode}/?__a=1\")\n    suspend fun getPost(@Path(\"shortcode\") shortcode: String): String\n\n    @GET(\"/explore/tags/{tag}/\")\n    suspend fun getTag(@Path(\"tag\") tag: String): String\n\n    @GET(\"/explore/locations/{locationId}/\")\n    suspend fun getLocation(@Path(\"locationId\") locationId: Long): String\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/LocationRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.LocationFeedResponse;\nimport awais.instagrabber.repositories.responses.Place;\nimport retrofit2.Call;\nimport retrofit2.http.GET;\nimport retrofit2.http.Path;\nimport retrofit2.http.QueryMap;\n\npublic interface LocationRepository {\n    @GET(\"/api/v1/locations/{location}/info/\")\n    Call<Place> fetch(@Path(\"location\") final long locationId);\n\n    @GET(\"/api/v1/feed/location/{location}/\")\n    Call<LocationFeedResponse> fetchPosts(@Path(\"location\") final long locationId,\n                                          @QueryMap Map<String, String> queryParams);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/MediaService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.LikersResponse\nimport awais.instagrabber.repositories.responses.MediaInfoResponse\nimport retrofit2.http.*\n\ninterface MediaService {\n    @GET(\"/api/v1/media/{mediaId}/info/\")\n    suspend fun fetch(@Path(\"mediaId\") mediaId: Long): MediaInfoResponse\n\n    @GET(\"/api/v1/media/{mediaId}/{action}/\")\n    suspend fun fetchLikes(\n        @Path(\"mediaId\") mediaId: String, // one of \"likers\" or \"comment_likers\"\n        @Path(\"action\") action: String,\n    ): LikersResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/{action}/\")\n    suspend fun action(\n        @Path(\"action\") action: String,\n        @Path(\"mediaId\") mediaId: String,\n        @FieldMap signedForm: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/edit_media/\")\n    suspend fun editCaption(\n        @Path(\"mediaId\") mediaId: String,\n        @FieldMap signedForm: Map<String, String>,\n    ): String\n\n    @GET(\"/api/v1/language/translate/\")\n    suspend fun translate(@QueryMap form: Map<String, String>): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/upload_finish/\")\n    suspend fun uploadFinish(\n        @Header(\"retry_context\") retryContext: String,\n        @QueryMap queryParams: Map<String, String>,\n        @FieldMap signedForm: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/delete/\")\n    suspend fun delete(\n        @Path(\"mediaId\") mediaId: String,\n        @Query(\"media_type\") mediaType: String,\n        @FieldMap signedForm: Map<String, String>,\n    ): String\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{mediaId}/archive/\")\n    suspend fun archive(\n        @Path(\"mediaId\") mediaId: String,\n        @FieldMap signedForm: Map<String, String>,\n    ): String\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/NewsRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.AymlResponse;\nimport awais.instagrabber.repositories.responses.NewsInboxResponse;\nimport awais.instagrabber.repositories.responses.UserSearchResponse;\nimport retrofit2.Call;\nimport retrofit2.http.FieldMap;\nimport retrofit2.http.FormUrlEncoded;\nimport retrofit2.http.GET;\nimport retrofit2.http.Header;\nimport retrofit2.http.POST;\nimport retrofit2.http.Query;\n\npublic interface NewsRepository {\n    @GET(\"/api/v1/news/inbox/\")\n    Call<NewsInboxResponse> appInbox(@Query(value = \"mark_as_seen\", encoded = true) boolean markAsSeen,\n                                     @Header(value = \"x-ig-app-id\") String xIgAppId);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/discover/ayml/\")\n    Call<AymlResponse> getAyml(@FieldMap final Map<String, String> form);\n\n    @GET(\"/api/v1/discover/chaining/\")\n    Call<UserSearchResponse> getChaining(@Query(value = \"target_id\") long targetId);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/ProfileService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.UserFeedResponse\nimport awais.instagrabber.repositories.responses.WrappedFeedResponse\nimport awais.instagrabber.repositories.responses.saved.CollectionsListResponse\nimport retrofit2.Call\nimport retrofit2.http.*\n\ninterface ProfileService {\n    @GET(\"/api/v1/feed/user/{uid}/\")\n    suspend fun fetch(\n        @Path(\"uid\") uid: Long,\n        @QueryMap queryParams: Map<String?, String?>?\n    ): UserFeedResponse?\n\n    @GET(\"/api/v1/feed/saved/\")\n    suspend fun fetchSaved(@QueryMap queryParams: Map<String?, String?>?): WrappedFeedResponse?\n\n    @GET(\"/api/v1/feed/collection/{collectionId}/\")\n    suspend fun fetchSavedCollection(\n        @Path(\"collectionId\") collectionId: String?,\n        @QueryMap queryParams: Map<String?, String?>?\n    ): WrappedFeedResponse?\n\n    @GET(\"/api/v1/feed/liked/\")\n    suspend fun fetchLiked(@QueryMap queryParams: Map<String?, String?>?): UserFeedResponse?\n\n    @GET(\"/api/v1/usertags/{profileId}/feed/\")\n    suspend fun fetchTagged(\n        @Path(\"profileId\") profileId: Long,\n        @QueryMap queryParams: Map<String?, String?>?\n    ): UserFeedResponse?\n\n    @GET(\"/api/v1/collections/list/\")\n    suspend fun fetchCollections(@QueryMap queryParams: Map<String?, String?>?): CollectionsListResponse?\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/collections/create/\")\n    suspend fun createCollection(@FieldMap signedForm: Map<String?, String?>?): String?\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/SearchService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.search.SearchResponse\nimport retrofit2.Call\nimport retrofit2.http.GET\nimport retrofit2.http.QueryMap\nimport retrofit2.http.Url\n\ninterface SearchService {\n    @GET\n    suspend fun search(\n        @Url url: String?,\n        @QueryMap(encoded = true) queryParams: Map<String?, String?>?\n    ): SearchResponse\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/StoriesService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.stories.ArchiveResponse\nimport awais.instagrabber.repositories.responses.stories.ReelsMediaResponse\nimport awais.instagrabber.repositories.responses.stories.ReelsResponse\nimport awais.instagrabber.repositories.responses.stories.ReelsTrayResponse\nimport awais.instagrabber.repositories.responses.stories.StoryMediaResponse\nimport awais.instagrabber.repositories.responses.stories.StoryStickerResponse\nimport retrofit2.http.*\n\ninterface StoriesService {\n    // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story\n    @GET(\"/api/v1/media/{mediaId}/info/\")\n    suspend fun fetch(@Path(\"mediaId\") mediaId: Long): StoryMediaResponse\n\n    @GET(\"/api/v1/feed/reels_tray/\")\n    suspend fun getFeedStories(): ReelsTrayResponse?\n\n    @GET(\"/api/v1/highlights/{uid}/highlights_tray/\")\n    suspend fun fetchHighlights(@Path(\"uid\") uid: Long): ReelsTrayResponse?\n\n    @GET(\"/api/v1/archive/reel/day_shells/\")\n    suspend fun fetchArchive(@QueryMap queryParams: Map<String, String>): ArchiveResponse?\n\n    @GET(\"/api/v1/feed/reels_media/\")\n    suspend fun getReelsMedia(@Query(\"user_ids\") id: String): ReelsMediaResponse\n\n    @GET(\"/api/v1/{type}/{id}/story/\")\n    suspend fun getStories(@Path(\"type\") type: String, @Path(\"id\") id: String): ReelsResponse\n\n    @GET(\"/api/v1/feed/user/{id}/story/\")\n    suspend fun getUserStories(@Path(\"id\") id: Long): ReelsResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/media/{storyId}/{stickerId}/{action}/\")\n    suspend fun respondToSticker(\n        @Path(\"storyId\") storyId: Long,\n        @Path(\"stickerId\") stickerId: Long,\n        @Path(\"action\") action: String,  // story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer\n        @FieldMap form: Map<String, String>,\n    ): StoryStickerResponse\n\n    @FormUrlEncoded\n    @POST(\"/api/v2/media/seen/\")\n    suspend fun seen(\n        @QueryMap queryParams: Map<String, String>,\n        @FieldMap form: Map<String, String>,\n    ): String\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/TagsRepository.java",
    "content": "package awais.instagrabber.repositories;\n\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.TagFeedResponse;\nimport retrofit2.Call;\nimport retrofit2.http.FieldMap;\nimport retrofit2.http.FormUrlEncoded;\nimport retrofit2.http.GET;\nimport retrofit2.http.Header;\nimport retrofit2.http.POST;\nimport retrofit2.http.Path;\nimport retrofit2.http.QueryMap;\n\npublic interface TagsRepository {\n    @GET(\"/api/v1/tags/{tag}/info/\")\n    Call<Hashtag> fetch(@Path(\"tag\") final String tag);\n\n    @FormUrlEncoded\n    @POST(\"/api/v1/tags/{action}/{tag}/\")\n    Call<String> changeFollow(@FieldMap final Map<String, String> signedForm,\n                              @Path(\"action\") String action,\n                              @Path(\"tag\") String tag);\n\n    @GET(\"/api/v1/feed/tag/{tag}/\")\n    Call<TagFeedResponse> fetchPosts(@Path(\"tag\") final String tag,\n                                     @QueryMap Map<String, String> queryParams);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/UserService.kt",
    "content": "package awais.instagrabber.repositories\n\nimport awais.instagrabber.repositories.responses.FriendshipStatus\nimport awais.instagrabber.repositories.responses.UserSearchResponse\nimport awais.instagrabber.repositories.responses.WrappedUser\nimport retrofit2.http.GET\nimport retrofit2.http.Path\nimport retrofit2.http.Query\n\ninterface UserService {\n    @GET(\"/api/v1/users/{uid}/info/\")\n    suspend fun getUserInfo(@Path(\"uid\") uid: Long): WrappedUser\n\n    @GET(\"/api/v1/users/{username}/usernameinfo/\")\n    suspend fun getUsernameInfo(@Path(\"username\") username: String): WrappedUser\n\n    @GET(\"/api/v1/friendships/show/{uid}/\")\n    suspend fun getUserFriendship(@Path(\"uid\") uid: Long): FriendshipStatus\n\n    @GET(\"/api/v1/users/search/\")\n    suspend fun search(\n        @Query(\"timezone_offset\") timezoneOffset: Float,\n        @Query(\"q\") query: String,\n    ): UserSearchResponse\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java",
    "content": "package awais.instagrabber.repositories.requests;\n\nimport java.io.Serializable;\n\npublic class StoryViewerOptions implements Serializable {\n    private final long id;\n    private final String name;\n    private final Type type;\n    private int currentFeedStoryIndex;\n\n    private StoryViewerOptions(final int position, final Type type) {\n        id = 0;\n        name = null;\n        this.currentFeedStoryIndex = position;\n        this.type = type;\n    }\n\n    private StoryViewerOptions(final String name, final Type type) {\n        this.name = name;\n        this.id = 0;\n        this.type = type;\n    }\n\n    private StoryViewerOptions(final long id, final Type type) {\n        this.name = null;\n        this.id = id;\n        this.type = type;\n    }\n\n    private StoryViewerOptions(final long id, final String name, final Type type) {\n        this.id = id;\n        this.name = name;\n        this.type = type;\n    }\n\n    public static StoryViewerOptions forHashtag(final String name) {\n        return new StoryViewerOptions(name, Type.HASHTAG);\n    }\n\n    public static StoryViewerOptions forLocation(final long id, final String name) {\n        return new StoryViewerOptions(id, name, Type.LOCATION);\n    }\n\n    public static StoryViewerOptions forUser(final long id, final String name) {\n        return new StoryViewerOptions(id, name, Type.USER);\n    }\n\n    public static StoryViewerOptions forHighlight(final long id, final String highlight) {\n        return new StoryViewerOptions(id, highlight, Type.HIGHLIGHT);\n    }\n\n    public static StoryViewerOptions forStory(final long mediaId, final String username) {\n        return new StoryViewerOptions(mediaId, username, Type.STORY);\n    }\n\n    public static StoryViewerOptions forFeedStoryPosition(final int position) {\n        return new StoryViewerOptions(position, Type.FEED_STORY_POSITION);\n    }\n\n    public static StoryViewerOptions forStoryArchive(final String id) {\n        return new StoryViewerOptions(id, Type.STORY_ARCHIVE);\n    }\n\n    public static StoryViewerOptions forStoryArchive(final int position) {\n        return new StoryViewerOptions(position, Type.STORY_ARCHIVE);\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Type getType() {\n        return type;\n    }\n\n    public int getCurrentFeedStoryIndex() {\n        return currentFeedStoryIndex;\n    }\n\n    public void setCurrentFeedStoryIndex(final int index) {\n        this.currentFeedStoryIndex = index;\n    }\n\n    public enum Type {\n        HASHTAG,\n        LOCATION,\n        USER,\n        HIGHLIGHT,\n        STORY,\n        FEED_STORY_POSITION,\n        STORY_ARCHIVE\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/UploadFinishOptions.kt",
    "content": "package awais.instagrabber.repositories.requests\n\ndata class UploadFinishOptions(\n    val uploadId: String,\n    val sourceType: String,\n    val videoOptions: VideoOptions? = null\n)\n\ndata class VideoOptions(\n    val length: Float = 0f,\n    var clips: List<Clip> = emptyList(),\n    val posterFrameIndex: Int = 0,\n    val isAudioMuted: Boolean = false\n) {\n    val map: Map<String, Any>\n        get() = mapOf(\n            \"length\" to length,\n            \"clips\" to clips,\n            \"poster_frame_index\" to posterFrameIndex,\n            \"audio_muted\" to isAudioMuted\n        )\n}\n\ndata class Clip(\n    val length: Float = 0f,\n    val sourceType: String\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/AnimatedMediaBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif\n\nclass AnimatedMediaBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val giphyGif: GiphyGif\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.ANIMATED_MEDIA\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"is_sticker\" to giphyGif.isSticker.toString(),\n            \"id\" to giphyGif.id\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/BroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nsealed class BroadcastOptions(\n    val clientContext: String,\n    private val threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val itemType: BroadcastItemType\n) {\n    var repliedToItemId: String? = null\n    var repliedToClientContext: String? = null\n    val threadIds: List<String>?\n        get() = threadIdsOrUserIds.threadIds\n    val userIds: List<List<String>>?\n        get() = threadIdsOrUserIds.userIds\n\n    abstract val formMap: Map<String, String>\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/LinkBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport org.json.JSONArray\n\nclass LinkBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val linkText: String,\n    val urls: List<String>\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.LINK\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"link_text\" to linkText,\n            \"link_urls\" to JSONArray(urls).toString()\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/MediaShareBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass MediaShareBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val mediaId: String,\n    val childId: String?\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.MEDIA_SHARE\n) {\n    override val formMap: Map<String, String>\n        get() = listOfNotNull(\n            \"media_id\" to mediaId,\n            if (childId != null) \"carousel_share_child_media_id\" to childId else null\n        ).toMap()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/PhotoBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass PhotoBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val allowFullAspectRatio: Boolean,\n    val uploadId: String\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.IMAGE\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"allow_full_aspect_ratio\" to allowFullAspectRatio.toString(),\n            \"upload_id\" to uploadId\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/ProfileBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass ProfileBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val profileId: String\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.PROFILE\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\"profile_user_id\" to profileId)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/ReactionBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass ReactionBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val itemId: String,\n    val emoji: String?,\n    val delete: Boolean\n) : BroadcastOptions(clientContext, threadIdsOrUserIds, BroadcastItemType.REACTION) {\n    override val formMap: Map<String, String>\n        get() = listOfNotNull(\n            \"item_id\" to itemId,\n            \"reaction_status\" to if (delete) \"deleted\" else \"created\",\n            \"reaction_type\" to \"like\",\n            if (!emoji.isNullOrBlank()) \"emoji\" to emoji else null,\n        ).toMap()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/StoryBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass StoryBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val mediaId: String,\n    val reelId: String\n) : BroadcastOptions(clientContext, threadIdsOrUserIds, BroadcastItemType.STORY) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"story_media_id\" to mediaId,\n            \"reel_id\" to reelId,\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/StoryReplyBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass StoryReplyBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val text: String,\n    val mediaId: String,\n    val reelId: String\n) : BroadcastOptions(clientContext, threadIdsOrUserIds, BroadcastItemType.REELSHARE) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"text\" to text,\n            \"media_id\" to mediaId,\n            \"reel_id\" to reelId,\n            \"entry\" to \"reel\",\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/TextBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass TextBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val text: String\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.TEXT\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\"text\" to text)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/ThreadIdsOrUserIds.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\ndata class ThreadIdsOrUserIds(val threadIds: List<String>? = null, val userIds: List<List<String>>? = null) {\n    companion object {\n        @JvmStatic\n        fun of(threadId: String): ThreadIdsOrUserIds {\n            return ThreadIdsOrUserIds(listOf(threadId), null)\n        }\n\n        fun ofOneUser(userId: String): ThreadIdsOrUserIds {\n            return ThreadIdsOrUserIds(null, listOf(listOf(userId)))\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/VideoBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\n\nclass VideoBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val videoResult: String,\n    val uploadId: String,\n    val sampled: Boolean\n) : BroadcastOptions(\n    clientContext,\n    threadIdsOrUserIds,\n    BroadcastItemType.VIDEO\n) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"video_result\" to videoResult,\n            \"upload_id\" to uploadId,\n            \"sampled\" to sampled.toString()\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/requests/directmessages/VoiceBroadcastOptions.kt",
    "content": "package awais.instagrabber.repositories.requests.directmessages\n\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport org.json.JSONArray\n\nclass VoiceBroadcastOptions(\n    clientContext: String,\n    threadIdsOrUserIds: ThreadIdsOrUserIds,\n    val uploadId: String,\n    val waveform: List<Float>,\n    val waveformSamplingFrequencyHz: Int\n) : BroadcastOptions(clientContext, threadIdsOrUserIds, BroadcastItemType.VOICE) {\n    override val formMap: Map<String, String>\n        get() = mapOf(\n            \"waveform\" to JSONArray(waveform).toString(),\n            \"upload_id\" to uploadId,\n            \"waveform_sampling_frequency_hz\" to waveformSamplingFrequencyHz.toString()\n        )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/AnimatedMediaFixedHeight.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class AnimatedMediaFixedHeight(val height: Int, val width: Int, val mp4: String?, val url: String?, val webp: String?) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/AnimatedMediaImages.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class AnimatedMediaImages(val fixedHeight: AnimatedMediaFixedHeight?) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Audio.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class Audio(\n    val audioSrc: String?,\n    val duration: Long,\n    val waveformData: List<Float>?,\n    val waveformSamplingFrequencyHz: Int,\n    val audioSrcExpirationTimestampUs: Long\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/AymlResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class AymlResponse(val newSuggestedUsers: AymlUserList?, val suggestedUsers: AymlUserList?) : Serializable\n\ndata class AymlUser(\n    val user: User?,\n    val algorithm: String?,\n    val socialContext: String?,\n    val uuid: String?\n) : Serializable\n\ndata class AymlUserList(val suggestions: List<AymlUser>?) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Caption.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class Caption(\n    val userId: Long = 0,\n    var text: String? = null,\n) : Serializable {\n    var pk: String? = null\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/ChildCommentsFetchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport awais.instagrabber.models.Comment\n\ndata class ChildCommentsFetchResponse(\n    val childCommentCount: Int,\n    val nextMaxChildCursor: String?,\n    val childComments: List<Comment>?,\n    val hasMoreTailChildComments: Boolean?\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/CommentsFetchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport awais.instagrabber.models.Comment\n\ndata class CommentsFetchResponse(\n    val commentCount: Int,\n    val nextMinId: String?,\n    val comments: List<Comment>?\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/FriendshipChangeResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class FriendshipChangeResponse(val friendshipStatus: FriendshipStatus?, val status: String?)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/FriendshipListFetchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class FriendshipListFetchResponse(\n    var nextMaxId: String?,\n    var status: String?,\n    var users: List<User>?\n) {\n    val isMoreAvailable: Boolean\n        get() = !nextMaxId.isNullOrBlank()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/FriendshipRestrictResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class FriendshipRestrictResponse(val users: List<User>?, val status: String?)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/FriendshipStatus.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class FriendshipStatus(\n    val following: Boolean = false,\n    val followedBy: Boolean = false,\n    val blocking: Boolean = false,\n    val muting: Boolean = false,\n    val isPrivate: Boolean = false,\n    val incomingRequest: Boolean = false,\n    val outgoingRequest: Boolean = false,\n    val isBestie: Boolean = false,\n    val isRestricted: Boolean = false,\n    val isMutingReel: Boolean = false,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/GraphQLUserListFetchResponse.java",
    "content": "package awais.instagrabber.repositories.responses;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.utils.TextUtils;\n\npublic class GraphQLUserListFetchResponse {\n    private String nextMaxId;\n    private String status;\n    private List<User> items;\n\n    public GraphQLUserListFetchResponse(final String nextMaxId,\n                                        final String status,\n                                        final List<User> items) {\n        this.nextMaxId = nextMaxId;\n        this.status = status;\n        this.items = items;\n    }\n\n    public boolean isMoreAvailable() {\n        return !TextUtils.isEmpty(nextMaxId);\n    }\n\n    public String getNextMaxId() {\n        return nextMaxId;\n    }\n\n    public GraphQLUserListFetchResponse setNextMaxId(final String nextMaxId) {\n        this.nextMaxId = nextMaxId;\n        return this;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public GraphQLUserListFetchResponse setStatus(final String status) {\n        this.status = status;\n        return this;\n    }\n\n    public List<User> getItems() {\n        return items;\n    }\n\n    public GraphQLUserListFetchResponse setItems(final List<User> items) {\n        this.items = items;\n        return this;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GraphQLUserListFetchResponse that = (GraphQLUserListFetchResponse) o;\n        return Objects.equals(nextMaxId, that.nextMaxId) &&\n                Objects.equals(status, that.status) &&\n                Objects.equals(items, that.items);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(nextMaxId, status, items);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"GraphQLUserListFetchResponse{\" +\n                \"nextMaxId='\" + nextMaxId + '\\'' +\n                \", status='\" + status + '\\'' +\n                \", items=\" + items +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport awais.instagrabber.models.enums.FollowingType\nimport java.io.Serializable\n\ndata class Hashtag(\n    val id: String,\n    val name: String,\n    val mediaCount: Long,\n    val following: FollowingType?, // 0 false 1 true; not on search results\n    val searchResultSubtitle: String? // shows how many posts there are on search results\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/ImageUrl.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class ImageUrl(val url: String, private val width: Int, private val height: Int) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/ImageVersions2.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class ImageVersions2(val candidates: List<MediaCandidate>) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/LikersResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class LikersResponse(val users: List<User>, val userCount: Long, val status: String)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Location.java",
    "content": "package awais.instagrabber.repositories.responses;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\npublic class Location implements Serializable {\n    private final long pk;\n    private final String shortName;\n    private final String name;\n    private final String address;\n    private final String city;\n    private final double lng;\n    private final double lat;\n\n    public Location(final long pk,\n                    final String shortName,\n                    final String name,\n                    final String address,\n                    final String city,\n                    final double lng,\n                    final double lat) {\n        this.pk = pk;\n        this.shortName = shortName;\n        this.name = name;\n        this.address = address;\n        this.city = city;\n        this.lng = lng;\n        this.lat = lat;\n    }\n\n    public long getPk() {\n        return pk;\n    }\n\n    public String getShortName() {\n        return shortName;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getAddress() {\n        return address;\n    }\n\n    public String getCity() {\n        return city;\n    }\n\n    public double getLng() {\n        return lng;\n    }\n\n    public double getLat() {\n        return lat;\n    }\n\n    public String getGeo() { return \"geo:\" + lat + \",\" + lng + \"?z=17&q=\" + lat + \",\" + lng + \"(\" + name + \")\"; }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final Location location = (Location) o;\n        return pk == location.pk &&\n                Double.compare(location.lng, lng) == 0 &&\n                Double.compare(location.lat, lat) == 0 &&\n                Objects.equals(shortName, location.shortName) &&\n                Objects.equals(name, location.name) &&\n                Objects.equals(address, location.address) &&\n                Objects.equals(city, location.city);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(pk, shortName, name, address, city, lng, lat);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/LocationFeedResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class LocationFeedResponse(\n    val numResults: Int,\n    val nextMaxId: String?,\n    val moreAvailable: Boolean?,\n    val mediaCount: Long?,\n    val status: String,\n    val items: List<Media>?,\n    val location: Location\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Media.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.models.enums.MediaItemType.Companion.valueOf\nimport awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator\nimport awais.instagrabber.utils.TextUtils\nimport java.io.Serializable\n\ndata class Media(\n    val pk: String? = null,\n    val id: String? = null,\n    val code: String? = null,\n    val takenAt: Long = -1,\n    val user: User? = null,\n    val canViewerReshare: Boolean = false,\n    val imageVersions2: ImageVersions2? = null,\n    val originalWidth: Int = 0,\n    val originalHeight: Int = 0,\n    val mediaType: Int = 0,\n    val commentLikesEnabled: Boolean = false,\n    val commentsDisabled: Boolean = false,\n    val nextMaxId: Long = -1,\n    val commentCount: Long = 0,\n    var likeCount: Long = 0,\n    var hasLiked: Boolean = false,\n    val isReelMedia: Boolean = false,\n    val videoVersions: List<MediaCandidate>? = null,\n    val hasAudio: Boolean = false,\n    val videoDuration: Double = 0.0,\n    val viewCount: Long = 0,\n    var caption: Caption? = null,\n    val canViewerSave: Boolean = false,\n    val audio: Audio? = null,\n    val title: String? = null,\n    val carouselMedia: List<Media>? = null,\n    val location: Location? = null,\n    val usertags: Usertags? = null,\n    var isSidecarChild: Boolean = false,\n    var hasViewerSaved: Boolean = false,\n    private val injected: Map<String, Any>? = null,\n    val endOfFeedDemarcator: EndOfFeedDemarcator? = null,\n    val carouselShareChildMediaId: String? = null // which specific child should dm show first\n) : Serializable {\n    private var dateString: String? = null\n\n    fun isInjected(): Boolean {\n        return injected != null\n    }\n\n    // TODO use extension once all usages are converted to kotlin\n    // val date: String by lazy {\n    //     if (takenAt <= 0) \"\" else Utils.datetimeParser.format(Date(takenAt * 1000L))\n    // }\n    val date: String\n        get() {\n            if (takenAt <= 0) return \"\"\n            if (dateString != null) return dateString ?: \"\"\n            dateString = TextUtils.epochSecondToString(takenAt)\n            return dateString ?: \"\"\n        }\n\n    val type: MediaItemType?\n        get() = valueOf(mediaType)\n\n    fun setPostCaption(caption: String?) {\n        var caption1: Caption? = this.caption\n        if (caption1 == null) {\n            user ?: return\n            caption1 = Caption(userId = user.pk, text = caption ?: \"\")\n            this.caption = caption1\n            return\n        }\n        caption1.text = caption ?: \"\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/MediaCandidate.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\ndata class MediaCandidate(val width: Int, val height: Int, val url: String) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/MediaInfoResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class MediaInfoResponse(val items: List<Media>)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport awais.instagrabber.repositories.responses.notification.Notification\nimport awais.instagrabber.repositories.responses.notification.NotificationCounts\n\ndata class NewsInboxResponse(\n    val counts: NotificationCounts,\n    val newStories: List<Notification>,\n    val oldStories: List<Notification>\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Place.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class Place(\n    val location: Location,\n    // for search\n    val title: String, // those are repeated within location\n    val subtitle: String?, // address\n    // browser only; for end of address\n    val slug: String?,\n    // for location info\n    val status: String?\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/PostsFetchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nclass PostsFetchResponse(\n    val feedModels: List<Media>,\n    val hasNextPage: Boolean,\n    val nextCursor: String?\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/TagFeedResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nclass TagFeedResponse(\n    val numResults: Int,\n    val nextMaxId: String?,\n    val moreAvailable: Boolean,\n    val status: String,\n    val items: List<Media>\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/User.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nimport java.io.Serializable\n\n\ndata class User @JvmOverloads constructor(\n    val pk: Long = 0,\n    val username: String = \"\",\n    val fullName: String? = \"\",\n    val isPrivate: Boolean = false,\n    val profilePicUrl: String? = null,\n    val isVerified: Boolean = false,\n    val profilePicId: String? = null,\n    var friendshipStatus: FriendshipStatus? = null,\n    val hasAnonymousProfilePicture: Boolean = false,\n    val isUnpublished: Boolean = false,\n    val isFavorite: Boolean = false,\n    val isDirectappInstalled: Boolean = false,\n    val hasChaining: Boolean = false,\n    val reelAutoArchive: String? = null,\n    val allowedCommenterType: String? = null,\n    val mediaCount: Long = 0,\n    val followerCount: Long = 0,\n    val followingCount: Long = 0,\n    val followingTagCount: Long = 0,\n    val biography: String? = null,\n    val externalUrl: String? = null,\n    val usertagsCount: Long = 0,\n    val publicEmail: String? = null,\n    val hdProfilePicUrlInfo: ImageUrl? = null,\n    val profileContext: String? = null, // \"also followed by\" your friends\n    val profileContextLinksWithUserIds: List<UserProfileContextLink>? = null, // ^\n    val socialContext: String? = null, // AYML\n    val interopMessagingUserFbid: String? = null, // in DMs only: Facebook user ID\n) : Serializable {\n    val hDProfilePicUrl: String\n        get() = hdProfilePicUrlInfo?.url ?: profilePicUrl ?: \"\"\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/UserFeedResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nclass UserFeedResponse(\n    val numResults: Int,\n    val nextMaxId: String?,\n    val moreAvailable: Boolean,\n    val status: String,\n    val items: List<Media>\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/UserProfileContextLink.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class UserProfileContextLink(\n    val username: String? = null,\n    val start: Int = 0,\n    val end: Int = 0,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/UserSearchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses\n\ndata class UserSearchResponse(\n    val numResults: Int,\n    val users: List<User>?,\n    val status: String\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/UsertagIn.java",
    "content": "package awais.instagrabber.repositories.responses;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class UsertagIn implements Serializable {\n    private final User user;\n    private final List<String> position;\n\n    public UsertagIn(final User user, final List<String> position) {\n        this.user = user;\n        this.position = position;\n    }\n\n    public User getUser() {\n        return user;\n    }\n\n    public List<String> getPosition() {\n        return position;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final UsertagIn usertagIn = (UsertagIn) o;\n        return Objects.equals(user, usertagIn.user) &&\n                Objects.equals(position, usertagIn.position);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(user, position);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/Usertags.java",
    "content": "package awais.instagrabber.repositories.responses;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Usertags implements Serializable {\n    private final List<UsertagIn> in;\n\n    public Usertags(final List<UsertagIn> in) {\n        this.in = in;\n    }\n\n    public List<UsertagIn> getIn() {\n        return in;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final Usertags usertags = (Usertags) o;\n        return Objects.equals(in, usertags.in);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(in);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/WrappedFeedResponse.java",
    "content": "package awais.instagrabber.repositories.responses;\n\nimport java.util.List;\n\npublic class WrappedFeedResponse {\n    private final int numResults;\n    private final String nextMaxId;\n    private final boolean moreAvailable;\n    private final String status;\n    private final List<WrappedMedia> items;\n\n    public WrappedFeedResponse(final int numResults,\n                               final String nextMaxId,\n                               final boolean moreAvailable,\n                               final String status,\n                               final List<WrappedMedia> items) {\n        this.numResults = numResults;\n        this.nextMaxId = nextMaxId;\n        this.moreAvailable = moreAvailable;\n        this.status = status;\n        this.items = items;\n    }\n\n    public int getNumResults() {\n        return numResults;\n    }\n\n    public String getNextMaxId() {\n        return nextMaxId;\n    }\n\n    public boolean isMoreAvailable() {\n        return moreAvailable;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public List<WrappedMedia> getItems() {\n        return items;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/WrappedMedia.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nclass WrappedMedia(val media: Media)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/WrappedUser.kt",
    "content": "package awais.instagrabber.repositories.responses\n\nclass WrappedUser(val user: User)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectBadgeCount.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectBadgeCount(\n    val userId: Long = 0,\n    val badgeCount: Int = 0,\n    val badgeCountAtMs: Long = 0,\n    val status: String? = null\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectInbox.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectInbox(\n    var threads: List<DirectThread>? = emptyList(),\n    val hasOlder: Boolean = false,\n    val unseenCount: Int = 0,\n    val unseenCountTs: String? = null,\n    val oldestCursor: String? = null,\n    val blendedInboxEnabled: Boolean\n) : Cloneable {\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any {\n        return super.clone()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectInboxResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.User\n\ndata class DirectInboxResponse(\n    val viewer: User? = null,\n    val inbox: DirectInbox? = null,\n    val seqId: Long = 0,\n    val snapshotAtMs: Long = 0,\n    val pendingRequestsTotal: Int = 0,\n    val hasPendingTopRequests: Boolean = false,\n    val mostRecentInviter: User? = null,\n    val status: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItem.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.models.enums.DirectItemType\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.User\nimport java.io.Serializable\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneId\n\ndata class DirectItem(\n    var itemId: String? = null,\n    val userId: Long = 0,\n    private var timestamp: Long = 0,\n    val itemType: DirectItemType? = null,\n    val text: String? = null,\n    val like: String? = null,\n    val link: DirectItemLink? = null,\n    val clientContext: String? = null,\n    val reelShare: DirectItemReelShare? = null,\n    val storyShare: DirectItemStoryShare? = null,\n    val mediaShare: Media? = null,\n    val profile: User? = null,\n    val placeholder: DirectItemPlaceholder? = null,\n    val media: Media? = null,\n    val previewMedias: List<Media>? = null,\n    val actionLog: DirectItemActionLog? = null,\n    val videoCallEvent: DirectItemVideoCallEvent? = null,\n    val clip: DirectItemClip? = null,\n    val felixShare: DirectItemFelixShare? = null,\n    val visualMedia: DirectItemVisualMedia? = null,\n    val animatedMedia: DirectItemAnimatedMedia? = null,\n    var reactions: DirectItemReactions? = null,\n    val repliedToMessage: DirectItem? = null,\n    val voiceMedia: DirectItemVoiceMedia? = null,\n    val location: Location? = null,\n    val xma: DirectItemXma? = null,\n    val hideInThread: Int? = 0,\n    val showForwardAttribution: Boolean = false\n) : Cloneable, Serializable {\n    var isPending = false\n    var date: LocalDateTime? = null\n        get() {\n            if (field == null) {\n                field = Instant.ofEpochMilli(timestamp / 1000).atZone(ZoneId.systemDefault()).toLocalDateTime()\n            }\n            return field\n        }\n        private set\n\n    fun getTimestamp(): Long {\n        return timestamp\n    }\n\n    fun setTimestamp(timestamp: Long) {\n        this.timestamp = timestamp\n        date = null\n    }\n\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any {\n        return super.clone()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemActionLog(\n    val description: String? = null,\n    val bold: List<TextRange>? = null,\n    val textAttributes: List<TextRange>? = null\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemAnimatedMedia.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.AnimatedMediaImages\nimport java.io.Serializable\n\ndata class DirectItemAnimatedMedia(\n    val id: String? = null,\n    val images: AnimatedMediaImages? = null,\n    val isRandom: Boolean = false,\n    val isSticker: Boolean = false,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemClip.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemClip(val clip: Media? = null) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemEmojiReaction.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemEmojiReaction(\n    val senderId: Long = 0,\n    val timestamp: Long = 0,\n    val emoji: String? = null,\n    val superReactType: String? = null\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemFelixShare.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemFelixShare(val video: Media? = null) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemLink.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemLink(\n    val text: String? = null,\n    val linkContext: DirectItemLinkContext? = null,\n    val clientContext: String? = null,\n    val mutationToken: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemLinkContext.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemLinkContext(\n    val linkUrl: String? = null,\n    val linkTitle: String? = null,\n    val linkSummary: String? = null,\n    val linkImageUrl: String? = null\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemPlaceholder.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemPlaceholder(\n    val isLinked: Boolean = false,\n    val title: String? = null,\n    val message: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReactions.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemReactions(\n    var emojis: List<DirectItemEmojiReaction>? = null,\n    var likes: List<DirectItemEmojiReaction>? = null,\n) : Cloneable, Serializable {\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any {\n        return super.clone()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReelShare.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemReelShare(\n    val text: String? = null,\n    val type: String? = null,\n    val reelOwnerId: Long = 0,\n    val mentionedUserId: Long = 0,\n    val isReelPersisted: Boolean = false,\n    val reelType: String? = null,\n    val media: Media? = null,\n    val reactionInfo: DirectItemReelShareReactionInfo? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReelShareReactionInfo.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemReelShareReactionInfo(\n    val emoji: String? = null,\n    val intensity: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemSeenResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectItemSeenResponse(\n    val action: String? = null,\n    val payload: DirectItemSeenResponsePayload? = null,\n    val status: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemSeenResponsePayload.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectItemSeenResponsePayload(val count: Int = 0, val timestamp: String? = null)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemStoryShare.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemStoryShare(\n    val reelId: String? = null,\n    val reelType: String? = null,\n    val text: String? = null,\n    val isReelPersisted: Boolean = false,\n    val media: Media? = null,\n    val title: String? = null,\n    val message: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVideoCallEvent.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemVideoCallEvent(\n    val action: String? = null,\n    val encodedServerDataInfo: String? = null,\n    val description: String? = null,\n    val threadHasAudioOnlyCall: Boolean = false,\n    val textAttributes: List<TextRange>? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVisualMedia.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.models.enums.RavenMediaViewMode\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemVisualMedia(\n    val urlExpireAtSecs: Long = 0,\n    val playbackDurationSecs: Int = 0,\n    val seenUserIds: List<Long>? = null,\n    val viewMode: RavenMediaViewMode? = null,\n    val seenCount: Int = 0,\n    val replayExpiringAtUs: Long = 0,\n    val expiringMediaActionSummary: RavenExpiringMediaActionSummary? = null,\n    val media: Media? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemVoiceMedia.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class DirectItemVoiceMedia(\n    val media: Media? = null,\n    val seenCount: Int = 0,\n    val viewMode: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemXma.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectItemXma(\n    val previewUrlInfo: XmaUrlInfo? = null,\n    val playableUrlInfo: XmaUrlInfo? = null,\n) : Serializable\n\ndata class XmaUrlInfo(\n    val url: String? = null,\n    val urlExpirationTimestampUs: Long = 0,\n    val width: Int = 0,\n    val height: Int = 0,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.User\nimport java.io.Serializable\n\ndata class DirectThread(\n    val threadId: String? = null,\n    val threadV2Id: String? = null,\n    var users: List<User>? = null,\n    var leftUsers: List<User>? = null,\n    var adminUserIds: List<Long>? = null,\n    var items: List<DirectItem>? = null,\n    val lastActivityAt: Long = 0,\n    var muted: Boolean = false,\n    val isPin: Boolean = false,\n    val named: Boolean = false,\n    val canonical: Boolean = false,\n    var pending: Boolean = false,\n    val archived: Boolean = false,\n    val valuedRequest: Boolean = false,\n    val threadType: String? = null,\n    val viewerId: Long = 0,\n    val threadTitle: String? = null,\n    val pendingScore: String? = null,\n    val folder: Long = 0,\n    val vcMuted: Boolean = false,\n    val isGroup: Boolean = false,\n    var mentionsMuted: Boolean = false,\n    val inviter: User? = null,\n    val hasOlder: Boolean = false,\n    val hasNewer: Boolean = false,\n    var lastSeenAt: Map<Long, DirectThreadLastSeenAt>? = null,\n    val newestCursor: String? = null,\n    val oldestCursor: String? = null,\n    val isSpam: Boolean = false,\n    val lastPermanentItem: DirectItem? = null,\n    val directStory: DirectThreadDirectStory? = null,\n    var approvalRequiredForNewMembers: Boolean = false,\n    var inputMode: Int = 0,\n    val threadContextItems: List<ThreadContext>? = null\n) : Serializable, Cloneable {\n    var isTemp = false\n\n    val firstDirectItem: DirectItem?\n        get() = items?.firstNotNullOfOrNull { it }\n\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any = super.clone()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadBroadcastResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectThreadBroadcastResponse(\n    val action: String? = null,\n    val statusCode: String? = null,\n    val payload: DirectThreadBroadcastResponsePayload? = null,\n    val messageMetadata: List<DirectThreadBroadcastResponseMessageMetadata>? = null,\n    val status: String? = null\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadBroadcastResponseMessageMetadata.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectThreadBroadcastResponseMessageMetadata(\n    val clientContext: String? = null,\n    val itemId: String? = null,\n    val timestamp: Long = 0,\n    val threadId: String? = null,\n    val participantIds: List<String>? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadBroadcastResponsePayload.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectThreadBroadcastResponsePayload(\n    val clientContext: String? = null,\n    val itemId: String? = null,\n    val timestamp: Long = 0,\n    val threadId: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadDetailsChangeResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectThreadDetailsChangeResponse(\n    val thread: DirectThread? = null,\n    val status: String? = null\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadDirectStory.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectThreadDirectStory(\n    val items: List<DirectItem>? = null,\n    val unseenCount: Int = 0,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadFeedResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class DirectThreadFeedResponse(\n    val thread: DirectThread? = null,\n    val status: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadLastSeenAt.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class DirectThreadLastSeenAt(\n    val timestamp: String? = null,\n    val itemId: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadParticipantRequestsResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.User\nimport java.io.Serializable\n\ndata class DirectThreadParticipantRequestsResponse(\n    var users: List<User>? = null,\n    val requesterUsernames: Map<Long, String>? = null,\n    val cursor: String? = null,\n    val totalThreadParticipants: Int = 0,\n    var totalParticipantRequests: Int = 0,\n    val status: String? = null,\n) : Serializable, Cloneable {\n    @Throws(CloneNotSupportedException::class)\n    public override fun clone(): Any = super.clone()\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/RankedRecipient.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport awais.instagrabber.repositories.responses.User\nimport java.io.Serializable\n\ndata class RankedRecipient(\n    val user: User? = null,\n    val thread: DirectThread? = null,\n) : Serializable {\n    companion object {\n        @JvmStatic\n        fun of(user: User): RankedRecipient {\n            return RankedRecipient(user = user)\n        }\n\n        @JvmStatic\n        fun of(thread: DirectThread): RankedRecipient {\n            return RankedRecipient(thread = thread)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/RankedRecipientsResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\ndata class RankedRecipientsResponse(\n    val rankedRecipients: List<RankedRecipient>? = null,\n    val expires: Long = 0,\n    val filtered: Boolean = false,\n    val requestId: String? = null,\n    val rankToken: String? = null,\n    val status: String? = null,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/RavenExpiringMediaActionSummary.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport com.google.gson.annotations.SerializedName\nimport java.io.Serializable\n\ndata class RavenExpiringMediaActionSummary(\n    val timestamp: Long = 0,\n    val count: Int = 0,\n    val type: ActionType? = null,\n) : Serializable\n\n// thanks to http://github.com/warifp/InstagramAutoPostImageUrl/blob/master/vendor/mgp25/instagram-php/src/Response/Model/ActionBadge.php\nenum class ActionType {\n    @SerializedName(\"raven_delivered\")\n    DELIVERED,\n\n    @SerializedName(\"raven_sent\")\n    SENT,\n\n    @SerializedName(\"raven_opened\")\n    OPENED,\n\n    @SerializedName(\"raven_screenshot\")\n    SCREENSHOT,\n\n    @SerializedName(\"raven_replayed\")\n    REPLAYED,\n\n    @SerializedName(\"raven_cannot_deliver\")\n    CANNOT_DELIVER,\n\n    @SerializedName(\"raven_sending\")\n    SENDING,\n\n    @SerializedName(\"raven_blocked\")\n    BLOCKED,\n\n    @SerializedName(\"raven_unknown\")\n    UNKNOWN,\n\n    @SerializedName(\"raven_suggested\")\n    SUGGESTED,\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/TextRange.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class TextRange(\n    val start: Int = 0,\n    val end: Int = 0,\n    val color: String? = null,\n    val intent: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/directmessages/ThreadContext.kt",
    "content": "package awais.instagrabber.repositories.responses.directmessages\n\nimport java.io.Serializable\n\ndata class ThreadContext(\n    val type: Int = 0,\n    val text: String? = null,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicCluster.kt",
    "content": "package awais.instagrabber.repositories.responses.discover\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\ndata class TopicCluster(\n    val id: String,\n    val title: String,\n    val type: String?,\n    val canMute: Boolean?,\n    val isMuted: Boolean?,\n    val rankedPosition: Int,\n    var coverMedia: Media?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicalExploreFeedResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.discover\n\nimport awais.instagrabber.repositories.responses.WrappedMedia\n\ndata class TopicalExploreFeedResponse(\n    val moreAvailable: Boolean,\n    val nextMaxId: String?,\n    val maxId: String?,\n    val status: String,\n    val numResults: Int,\n    val clusters: List<TopicCluster>?,\n    val items: List<WrappedMedia>?\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java",
    "content": "package awais.instagrabber.repositories.responses.feed;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\npublic class EndOfFeedDemarcator implements Serializable {\n    private final long id;\n    private final EndOfFeedGroupSet groupSet;\n\n    public EndOfFeedDemarcator(final long id, final EndOfFeedGroupSet groupSet) {\n        this.id = id;\n        this.groupSet = groupSet;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public EndOfFeedGroupSet getGroupSet() {\n        return groupSet;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final EndOfFeedDemarcator that = (EndOfFeedDemarcator) o;\n        return id == that.id &&\n                Objects.equals(groupSet, that.groupSet);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, groupSet);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java",
    "content": "package awais.instagrabber.repositories.responses.feed;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Objects;\n\nimport awais.instagrabber.repositories.responses.Media;\n\npublic class EndOfFeedGroup implements Serializable {\n    private final String id;\n    private final String title;\n    private final String nextMaxId;\n    private final List<Media> feedItems;\n\n    public EndOfFeedGroup(final String id, final String title, final String nextMaxId, final List<Media> feedItems) {\n        this.id = id;\n        this.title = title;\n        this.nextMaxId = nextMaxId;\n        this.feedItems = feedItems;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public String getNextMaxId() {\n        return nextMaxId;\n    }\n\n    public List<Media> getFeedItems() {\n        return feedItems;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final EndOfFeedGroup that = (EndOfFeedGroup) o;\n        return Objects.equals(id, that.id) &&\n                Objects.equals(title, that.title) &&\n                Objects.equals(nextMaxId, that.nextMaxId) &&\n                Objects.equals(feedItems, that.feedItems);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, title, nextMaxId, feedItems);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java",
    "content": "package awais.instagrabber.repositories.responses.feed;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class EndOfFeedGroupSet implements Serializable {\n    private final long id;\n    private final String activeGroupId;\n    private final String connectedGroupId;\n    private final String nextMaxId;\n    private final String paginationSource;\n    private final List<EndOfFeedGroup> groups;\n\n    public EndOfFeedGroupSet(final long id,\n                             final String activeGroupId,\n                             final String connectedGroupId,\n                             final String nextMaxId,\n                             final String paginationSource,\n                             final List<EndOfFeedGroup> groups) {\n        this.id = id;\n        this.activeGroupId = activeGroupId;\n        this.connectedGroupId = connectedGroupId;\n        this.nextMaxId = nextMaxId;\n        this.paginationSource = paginationSource;\n        this.groups = groups;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public String getActiveGroupId() {\n        return activeGroupId;\n    }\n\n    public String getConnectedGroupId() {\n        return connectedGroupId;\n    }\n\n    public String getNextMaxId() {\n        return nextMaxId;\n    }\n\n    public String getPaginationSource() {\n        return paginationSource;\n    }\n\n    public List<EndOfFeedGroup> getGroups() {\n        return groups;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final EndOfFeedGroupSet that = (EndOfFeedGroupSet) o;\n        return id == that.id &&\n                Objects.equals(activeGroupId, that.activeGroupId) &&\n                Objects.equals(connectedGroupId, that.connectedGroupId) &&\n                Objects.equals(nextMaxId, that.nextMaxId) &&\n                Objects.equals(paginationSource, that.paginationSource) &&\n                Objects.equals(groups, that.groups);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id, activeGroupId, connectedGroupId, nextMaxId, paginationSource, groups);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java",
    "content": "package awais.instagrabber.repositories.responses.feed;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.Media;\n\npublic class FeedFetchResponse {\n    private final List<Media> items;\n    private final int numResults;\n    private final boolean moreAvailable;\n    private final String nextMaxId;\n    private final String status;\n\n    public FeedFetchResponse(final List<Media> items,\n                             final int numResults,\n                             final boolean moreAvailable,\n                             final String nextMaxId,\n                             final String status) {\n        this.items = items;\n        this.numResults = numResults;\n        this.moreAvailable = moreAvailable;\n        this.nextMaxId = nextMaxId;\n        this.status = status;\n    }\n\n    public List<Media> getItems() {\n        return items;\n    }\n\n    public int getNumResults() {\n        return numResults;\n    }\n\n    public boolean isMoreAvailable() {\n        return moreAvailable;\n    }\n\n    public String getNextMaxId() {\n        return nextMaxId;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGif.java",
    "content": "package awais.instagrabber.repositories.responses.giphy;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.Objects;\n\npublic class GiphyGif {\n    private final String type;\n    private final String id;\n    private final String title;\n    private final int isSticker;\n    private final GiphyGifImages images;\n\n    public GiphyGif(final String type, final String id, final String title, final int isSticker, final GiphyGifImages images) {\n        this.type = type;\n        this.id = id;\n        this.title = title;\n        this.isSticker = isSticker;\n        this.images = images;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public boolean isSticker() {\n        return isSticker ==  1;\n    }\n\n    public GiphyGifImages getImages() {\n        return images;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GiphyGif giphyGif = (GiphyGif) o;\n        return isSticker == giphyGif.isSticker &&\n                Objects.equals(type, giphyGif.type) &&\n                Objects.equals(id, giphyGif.id) &&\n                Objects.equals(title, giphyGif.title) &&\n                Objects.equals(images, giphyGif.images);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(type, id, title, isSticker, images);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"GiphyGif{\" +\n                \"type='\" + type + '\\'' +\n                \", id='\" + id + '\\'' +\n                \", title='\" + title + '\\'' +\n                \", isSticker=\" + isSticker() +\n                \", images=\" + images +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifImage.java",
    "content": "package awais.instagrabber.repositories.responses.giphy;\n\nimport java.util.Objects;\n\npublic class GiphyGifImage {\n    private final int height;\n    private final int width;\n    private final long webpSize;\n    private final String webp;\n\n    public GiphyGifImage(final int height, final int width, final long webpSize, final String webp) {\n        this.height = height;\n        this.width = width;\n        this.webpSize = webpSize;\n        this.webp = webp;\n    }\n\n    public int getHeight() {\n        return height;\n    }\n\n    public int getWidth() {\n        return width;\n    }\n\n    public long getWebpSize() {\n        return webpSize;\n    }\n\n    public String getWebp() {\n        return webp;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GiphyGifImage that = (GiphyGifImage) o;\n        return height == that.height &&\n                width == that.width &&\n                webpSize == that.webpSize &&\n                Objects.equals(webp, that.webp);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(height, width, webpSize, webp);\n    }\n\n    @Override\n    public String toString() {\n        return \"GiphyGifImage{\" +\n                \"height=\" + height +\n                \", width=\" + width +\n                \", webpSize=\" + webpSize +\n                \", webp='\" + webp + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifImages.java",
    "content": "package awais.instagrabber.repositories.responses.giphy;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;\n\npublic class GiphyGifImages {\n    private final AnimatedMediaFixedHeight fixedHeight;\n\n    public GiphyGifImages(final AnimatedMediaFixedHeight fixedHeight) {\n        this.fixedHeight = fixedHeight;\n    }\n\n    public AnimatedMediaFixedHeight getFixedHeight() {\n        return fixedHeight;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GiphyGifImages that = (GiphyGifImages) o;\n        return Objects.equals(fixedHeight, that.fixedHeight);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(fixedHeight);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"GiphyGifImages{\" +\n                \"fixedHeight=\" + fixedHeight +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifResponse.java",
    "content": "package awais.instagrabber.repositories.responses.giphy;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.Objects;\n\npublic class GiphyGifResponse {\n    private final GiphyGifResults results;\n    private final String status;\n\n    public GiphyGifResponse(final GiphyGifResults results, final String status) {\n        this.results = results;\n        this.status = status;\n    }\n\n    public GiphyGifResults getResults() {\n        return results;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GiphyGifResponse that = (GiphyGifResponse) o;\n        return Objects.equals(results, that.results) &&\n                Objects.equals(status, that.status);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(results, status);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"GiphyGifResponse{\" +\n                \"results=\" + results +\n                \", status='\" + status + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifResults.java",
    "content": "package awais.instagrabber.repositories.responses.giphy;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.List;\nimport java.util.Objects;\n\npublic class GiphyGifResults {\n    private final List<GiphyGif> giphyGifs;\n    private final List<GiphyGif> giphy;\n\n    public GiphyGifResults(final List<GiphyGif> giphyGifs, final List<GiphyGif> giphy) {\n        this.giphyGifs = giphyGifs;\n        this.giphy = giphy;\n    }\n\n    public List<GiphyGif> getGiphyGifs() {\n        return giphyGifs;\n    }\n\n    public List<GiphyGif> getGiphy() {\n        return giphy;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final GiphyGifResults that = (GiphyGifResults) o;\n        return Objects.equals(giphyGifs, that.giphyGifs) &&\n                Objects.equals(giphy, that.giphy);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(giphyGifs, giphy);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"GiphyGifResults{\" +\n                \"giphyGifs=\" + giphyGifs +\n                \", giphy=\" + giphy +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.kt",
    "content": "package awais.instagrabber.repositories.responses.notification\n\nimport awais.instagrabber.models.enums.NotificationType\nimport awais.instagrabber.models.enums.NotificationType.Companion.valueOfType\n\nclass Notification(val args: NotificationArgs,\n                   private val storyType: Int,\n                   val pk: String?) {\n    val type: NotificationType?\n        get() = valueOfType(storyType)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java",
    "content": "package awais.instagrabber.repositories.responses.notification;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport awais.instagrabber.utils.TextUtils;\n\npublic class NotificationArgs {\n    private final String text;\n    private final String richText;\n    private final long profileId;\n    private final String profileImage;\n    private final List<NotificationImage> media;\n    private final double timestamp;\n    private final String profileName;\n    private final String fullName; // for AYML, not naturally generated\n    private final boolean isVerified; // mostly for AYML, not sure about notif\n\n    public NotificationArgs(final String text,\n                            final String richText, // for AYML, this is the algorithm\n                            final long profileId,\n                            final String profileImage,\n                            final List<NotificationImage> media,\n                            final double timestamp,\n                            final String profileName,\n                            final String fullName,\n                            final boolean isVerified) {\n        this.text = text;\n        this.richText = richText;\n        this.profileId = profileId;\n        this.profileImage = profileImage;\n        this.media = media;\n        this.timestamp = timestamp;\n        this.profileName = profileName;\n        this.fullName = fullName;\n        this.isVerified = isVerified;\n    }\n\n    public String getText() {\n        return text == null ? cleanRichText(richText) : text;\n    }\n\n    public long getUserId() {\n        return profileId;\n    }\n\n    public String getProfilePic() {\n        return profileImage;\n    }\n\n    public String getUsername() {\n        return profileName;\n    }\n\n    public String getFullName() {\n        return fullName;\n    }\n\n    public List<NotificationImage> getMedia() {\n        return media;\n    }\n\n    public double getTimestamp() {\n        return timestamp;\n    }\n\n    public boolean isVerified() {\n        return isVerified;\n    }\n\n    @NonNull\n    public String getDateTime() {\n        return TextUtils.epochSecondToString(Math.round(timestamp));\n    }\n\n    private String cleanRichText(final String raw) {\n        if (raw == null) return null;\n        final Matcher matcher = Pattern.compile(\"\\\\{[\\\\p{L}\\\\d._]+\\\\|000000\\\\|1\\\\|user\\\\?id=\\\\d+\\\\}\").matcher(raw);\n        String result = raw;\n        while (matcher.find()) {\n            final String richObject = raw.substring(matcher.start(), matcher.end());\n            final String username = richObject.split(\"\\\\|\")[0].substring(1);\n            result = result.replace(richObject, username);\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.kt",
    "content": "package awais.instagrabber.repositories.responses.notification\n\nclass NotificationCounts(val commentLikes: Int,\n                         val usertags: Int,\n                         val likes: Int,\n                         val comments: Int,\n                         val relationships: Int,\n                         val photosOfYou: Int,\n                         val requests: Int)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.kt",
    "content": "package awais.instagrabber.repositories.responses.notification\n\nclass NotificationImage(val id: String, val image: String)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/saved/CollectionsListResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.saved\n\nclass CollectionsListResponse     //        this.numResults = numResults;\n(val isMoreAvailable: Boolean,\n val nextMaxId: String,\n val maxId: String,\n val status: String,  //                                   final int numResults,\n        //    public int getNumResults() {\n        //        return numResults;\n        //    }\n        //    private final int numResults;\n val items: List<SavedCollection>)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/saved/SavedCollection.kt",
    "content": "package awais.instagrabber.repositories.responses.saved\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.io.Serializable\n\nclass SavedCollection(val collectionId: String,\n                      val collectionName: String,\n                      val collectionType: String,\n                      val collectionMediaCount: Int,\n                      // coverMedia or coverMediaList: only one is defined\n                      val coverMedia: Media?,\n                      val coverMediaList: List<Media>?) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java",
    "content": "package awais.instagrabber.repositories.responses.search;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.db.entities.RecentSearch;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Place;\nimport awais.instagrabber.repositories.responses.User;\n\npublic class SearchItem {\n    private static final String TAG = SearchItem.class.getSimpleName();\n\n    private final User user;\n    private final Place place;\n    private final Hashtag hashtag;\n    private final int position;\n\n    private boolean isRecent = false;\n    private boolean isFavorite = false;\n\n    public SearchItem(final User user,\n                      final Place place,\n                      final Hashtag hashtag,\n                      final int position) {\n        this.user = user;\n        this.place = place;\n        this.hashtag = hashtag;\n        this.position = position;\n    }\n\n    public User getUser() {\n        return user;\n    }\n\n    public Place getPlace() {\n        return place;\n    }\n\n    public Hashtag getHashtag() {\n        return hashtag;\n    }\n\n    public int getPosition() {\n        return position;\n    }\n\n    public boolean isRecent() {\n        return isRecent;\n    }\n\n    public void setRecent(final boolean recent) {\n        isRecent = recent;\n    }\n\n    public boolean isFavorite() {\n        return isFavorite;\n    }\n\n    public void setFavorite(final boolean favorite) {\n        isFavorite = favorite;\n    }\n\n    @Nullable\n    public FavoriteType getType() {\n        if (user != null) {\n            return FavoriteType.USER;\n        }\n        if (hashtag != null) {\n            return FavoriteType.HASHTAG;\n        }\n        if (place != null) {\n            return FavoriteType.LOCATION;\n        }\n        return null;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        final SearchItem that = (SearchItem) o;\n        return Objects.equals(user, that.user) &&\n                Objects.equals(place, that.place) &&\n                Objects.equals(hashtag, that.hashtag);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(user, place, hashtag);\n    }\n\n    @NonNull\n    @Override\n    public String toString() {\n        return \"SearchItem{\" +\n                \"user=\" + user +\n                \", place=\" + place +\n                \", hashtag=\" + hashtag +\n                \", position=\" + position +\n                \", isRecent=\" + isRecent +\n                '}';\n    }\n\n    @NonNull\n    public static List<SearchItem> fromRecentSearch(final List<RecentSearch> recentSearches) {\n        if (recentSearches == null) return Collections.emptyList();\n        return recentSearches.stream()\n                             .map(SearchItem::fromRecentSearch)\n                             .filter(Objects::nonNull)\n                             .collect(Collectors.toList());\n    }\n\n    @Nullable\n    private static SearchItem fromRecentSearch(final RecentSearch recentSearch) {\n        if (recentSearch == null) return null;\n        try {\n            final FavoriteType type = recentSearch.getType();\n            final SearchItem searchItem;\n            switch (type) {\n                case USER:\n                    searchItem = new SearchItem(getUser(recentSearch), null, null, 0);\n                    break;\n                case HASHTAG:\n                    searchItem = new SearchItem(null, null, getHashtag(recentSearch), 0);\n                    break;\n                case LOCATION:\n                    searchItem = new SearchItem(null, getPlace(recentSearch), null, 0);\n                    break;\n                default:\n                    return null;\n            }\n            searchItem.setRecent(true);\n            return searchItem;\n        } catch (Exception e) {\n            Log.e(TAG, \"fromRecentSearch: \", e);\n        }\n        return null;\n    }\n\n    public static List<SearchItem> fromFavorite(final List<Favorite> favorites) {\n        if (favorites == null) {\n            return Collections.emptyList();\n        }\n        return favorites.stream()\n                        .map(SearchItem::fromFavorite)\n                        .filter(Objects::nonNull)\n                        .collect(Collectors.toList());\n    }\n\n    @Nullable\n    private static SearchItem fromFavorite(final Favorite favorite) {\n        if (favorite == null) return null;\n        final FavoriteType type = favorite.getType();\n        if (type == null) return null;\n        final SearchItem searchItem;\n        switch (type) {\n            case USER:\n                searchItem = new SearchItem(getUser(favorite), null, null, 0);\n                break;\n            case HASHTAG:\n                searchItem = new SearchItem(null, null, getHashtag(favorite), 0);\n                break;\n            case LOCATION:\n                final Place place = getPlace(favorite);\n                if (place == null) return null;\n                searchItem = new SearchItem(null, place, null, 0);\n                break;\n            default:\n                return null;\n        }\n        searchItem.setFavorite(true);\n        return searchItem;\n    }\n\n    @NonNull\n    private static User getUser(@NonNull final RecentSearch recentSearch) {\n        return new User(\n                Long.parseLong(recentSearch.getIgId()),\n                recentSearch.getUsername(),\n                recentSearch.getName(),\n                false,\n                recentSearch.getPicUrl(),\n                false\n        );\n    }\n\n    @NonNull\n    private static User getUser(@NonNull final Favorite favorite) {\n        return new User(\n                0,\n                favorite.getQuery(),\n                favorite.getDisplayName(),\n                false,\n                favorite.getPicUrl(),\n                false\n        );\n    }\n\n    @NonNull\n    private static Hashtag getHashtag(@NonNull final RecentSearch recentSearch) {\n        return new Hashtag(\n                recentSearch.getIgId(),\n                recentSearch.getName(),\n                0,\n                null,\n                null\n        );\n    }\n\n    @NonNull\n    private static Hashtag getHashtag(@NonNull final Favorite favorite) {\n        return new Hashtag(\n                \"0\",\n                favorite.getQuery(),\n                0,\n                null,\n                null\n        );\n    }\n\n    @NonNull\n    private static Place getPlace(@NonNull final RecentSearch recentSearch) {\n        final Location location = new Location(\n                Long.parseLong(recentSearch.getIgId()),\n                recentSearch.getName(),\n                recentSearch.getName(),\n                null, null, 0, 0\n        );\n        return new Place(\n                location,\n                recentSearch.getName(),\n                null,\n                null,\n                null\n        );\n    }\n\n    @Nullable\n    private static Place getPlace(@NonNull final Favorite favorite) {\n        try {\n            final Location location = new Location(\n                    Long.parseLong(favorite.getQuery()),\n                    favorite.getDisplayName(),\n                    favorite.getDisplayName(),\n                    null, null, 0, 0\n            );\n            return new Place(\n                    location,\n                    favorite.getDisplayName(),\n                    null,\n                    null,\n                    null\n            );\n        } catch (Exception e) {\n            Log.e(TAG, \"getPlace: \", e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.search\n\ndata class SearchResponse(\n    // app\n    val list: List<SearchItem>?,\n    // browser\n    val users: List<SearchItem>?,\n    val places: List<SearchItem>?,\n    val hashtags: List<SearchItem>?,\n    // universal\n    val status: String?,\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/ArchiveResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\ndata class ArchiveResponse(\n    val numResults: Int,\n    val maxId: String?,\n    val moreAvailable: Boolean,\n    val status: String,\n    val items: List<Story>\n)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/Broadcast.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport awais.instagrabber.repositories.responses.User\nimport java.io.Serializable\n\ndata class Broadcast(\n    val id: String?,\n    val dashPlaybackUrl: String?,\n    val dashAbrPlaybackUrl: String?, // adaptive quality\n    val viewerCount: Double?, // always .0\n    val muted: Boolean?,\n    val coverFrameUrl: String?,\n    val broadcastOwner: User?,\n    val publishedTime: Long?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/CoverMedia.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport awais.instagrabber.repositories.responses.ImageUrl\n\ndata class CoverMedia(val croppedImageVersion: ImageUrl)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/PollSticker.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class PollSticker(\n    val pollId: Long,\n    val question: String?,\n    val tallies: List<Tally>,\n    var viewerVote: Int?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/QuestionSticker.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class QuestionSticker(\n    val questionType: String,\n    val questionId: Long,\n    val question: String\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/QuizSticker.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class QuizSticker(\n    val quizId: Long,\n    val question: String,\n    val tallies: List<Tally>,\n    var viewerAnswer: Int?,\n    val correctAnswer: Int\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/ReelsMediaResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\n\ndata class ReelsMediaResponse(\n    val status: String?,\n    val reels: Map<String, Story?>?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/ReelsResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\n\ndata class ReelsResponse(\n    val status: String?,\n    val reel: Story?, // users\n    val story: Story?, // hashtag and locations (unused)\n    val broadcast: Broadcast?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/ReelsTrayResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\n\ndata class ReelsTrayResponse(\n    val status: String?,\n    val tray: List<Story>?,\n    val broadcasts: List<Broadcast>?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/SliderSticker.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class SliderSticker(\n    val sliderId: Long,\n    val question: String,\n    val emoji: String?,\n    val viewerCanVote: Boolean?,\n    val viewerVote: Double?,\n    val sliderVoteAverage: Double?,\n    val sliderVoteCount: Int?,\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport awais.instagrabber.repositories.responses.ImageUrl\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.utils.TextUtils\nimport java.io.Serializable\n\ndata class Story(\n    // universal\n    val id: String? = null,\n    val latestReelMedia: Long? = null, // = timestamp\n    val mediaCount: Int? = null,\n    // for stories and highlights\n    val seen: Long? = null,\n    val user: User? = null,\n    // for stories\n    val muted: Boolean? = null,\n    val hasBestiesMedia: Boolean? = null,\n    val items: List<StoryMedia>? = null, // may be null\n    // for highlights\n    val coverMedia: CoverMedia? = null,\n    val title: String? = null,\n    // for archives\n    val coverImageVersion: ImageUrl? = null,\n    // invented fields\n    val broadcast: Broadcast? = null, // does not naturally occur\n) : Serializable {\n    val dateTime: String\n        get() = if (latestReelMedia != null) TextUtils.epochSecondToString(latestReelMedia) else \"\"\n    // note that archives have property \"timestamp\" but is ignored\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryAppAttribution.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport android.net.Uri\nimport java.io.Serializable\n\n// https://github.com/austinhuang0131/barinsta/issues/1151\ndata class StoryAppAttribution(\n    val name: String?,\n    val appActionText: String?,\n    val contentUrl: String?\n) : Serializable {\n    val url: String?\n        get() {\n            val uri = Uri.parse(contentUrl)\n            return if (uri.getHost().equals(\"open.spotify.com\")) contentUrl?.split(\"?\")?.get(0)\n                   else contentUrl\n        }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryCta.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class StoryCta(\n    val webUri: String?\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMedia.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.models.enums.MediaItemType.Companion.valueOf\nimport awais.instagrabber.repositories.responses.ImageVersions2\nimport awais.instagrabber.repositories.responses.MediaCandidate\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.utils.TextUtils\nimport java.io.Serializable\n\ndata class StoryMedia(\n        // inherited from Media\n    val pk: Long = -1,\n    val id: String = \"\",\n    val takenAt: Long = -1,\n    val user: User? = null,\n    val canReshare: Boolean = false,\n    val imageVersions2: ImageVersions2? = null,\n    val originalWidth: Int = 0,\n    val originalHeight: Int = 0,\n    val mediaType: Int = 0,\n    val isReelMedia: Boolean = false,\n    val videoVersions: List<MediaCandidate>? = null,\n    val hasAudio: Boolean = false,\n    val videoDuration: Double = 0.0,\n    val viewCount: Long = 0,\n    val title: String? = null,\n    // story-specific\n    val canReply: Boolean = false,\n    val linkText: String? = null, // required for story_cta\n    // stickers\n    val reelMentions: List<StorySticker>? = null,\n    val storyHashtags: List<StorySticker>? = null,\n    val storyLocations: List<StorySticker>? = null,\n    val storyFeedMedia: List<StorySticker>? = null,\n    val storyPolls: List<StorySticker>? = null,\n    val storyQuestions: List<StorySticker>? = null,\n    val storyQuizs: List<StorySticker>? = null,\n    val storyCta: List<StorySticker>? = null,\n    val storySliders: List<StorySticker>? = null,\n    // spotify/soundcloud button, not a sticker\n    val storyAppAttribution: StoryAppAttribution? = null\n) : Serializable {\n    private var dateString: String? = null\n    var position = 0\n    var isCurrentSlide = false\n\n    // TODO use extension once all usages are converted to kotlin\n    // val date: String by lazy {\n    //     if (takenAt <= 0) \"\" else Utils.datetimeParser.format(Date(takenAt * 1000L))\n    // }\n    val type: MediaItemType?\n        get() = valueOf(mediaType)\n\n    val date: String\n        get() {\n            if (takenAt <= 0) return \"\"\n            if (dateString != null) return dateString ?: \"\"\n            dateString = TextUtils.epochSecondToString(takenAt)\n            return dateString ?: \"\"\n        }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryMediaResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.repositories.responses.ImageVersions2\nimport awais.instagrabber.repositories.responses.MediaCandidate\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.utils.TextUtils\nimport java.io.Serializable\n\ndata class StoryMediaResponse(\n    val items: List<StoryMedia?>?, // length 1\n    val status: String?\n    // ignoring pagination properties\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StorySticker.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class StorySticker(\n    // only ONE object should exist\n    val user: User?, // reel_mentions\n    val hashtag: Hashtag?, // story_hashtags\n    val location: Location?, // story_locations\n    val mediaId: String?, // story_feed_media\n    val pollSticker: PollSticker?, // story_polls\n    val questionSticker: QuestionSticker?, // story_questions\n    val quizSticker: QuizSticker?, // story_quizs\n    val links: List<StoryCta?>?, // story_cta, requires link_text from the story\n    val sliderSticker: SliderSticker? // story_sliders\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/StoryStickerResponse.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\ndata class StoryStickerResponse(val status: String?)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/responses/stories/Tally.kt",
    "content": "package awais.instagrabber.repositories.responses.stories\n\nimport java.io.Serializable\nimport awais.instagrabber.repositories.responses.Hashtag\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.User\n\ndata class Tally(\n    val text: String,\n    val count: Int\n) : Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/repositories/serializers/CaptionDeserializer.java",
    "content": "package awais.instagrabber.repositories.serializers;\n\nimport android.util.Log;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonDeserializationContext;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParseException;\n\nimport java.lang.reflect.Type;\n\nimport awais.instagrabber.repositories.responses.Caption;\n\npublic class CaptionDeserializer implements JsonDeserializer<Caption> {\n\n    private static final String TAG = CaptionDeserializer.class.getSimpleName();\n\n    @Override\n    public Caption deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n        final Caption caption = new Gson().fromJson(json, Caption.class);\n        final JsonObject jsonObject = json.getAsJsonObject();\n        if (jsonObject.has(\"pk\")) {\n            JsonElement elem = jsonObject.get(\"pk\");\n            if (elem != null && !elem.isJsonNull()) {\n                if (!elem.isJsonPrimitive()) return caption;\n                String pkString = elem.getAsString();\n                if (pkString.contains(\"_\")) {\n                    pkString = pkString.substring(0, pkString.indexOf(\"_\"));\n                }\n                try {\n                    caption.setPk(pkString);\n                } catch (NumberFormatException e) {\n                    Log.e(TAG, \"deserialize: \", e);\n                }\n            }\n        }\n        return caption;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java",
    "content": "package awais.instagrabber.services;\n\nimport android.app.Notification;\nimport android.app.PendingIntent;\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.Binder;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.text.TextUtils;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.app.NotificationCompat;\nimport androidx.core.app.NotificationManagerCompat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.repositories.responses.notification.NotificationCounts;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.webservices.NewsService;\nimport awais.instagrabber.webservices.ServiceCallback;\n\npublic class ActivityCheckerService extends Service {\n    private static final String TAG = \"ActivityCheckerService\";\n    private static final int INITIAL_DELAY_MILLIS = 200;\n    private static final int DELAY_MILLIS = 60000;\n\n    private Handler handler;\n    private NewsService newsService;\n    private ServiceCallback<NotificationCounts> cb;\n    private NotificationManagerCompat notificationManager;\n\n    private final IBinder binder = new LocalBinder();\n    private final Runnable runnable = () -> {\n        newsService.fetchActivityCounts(cb);\n    };\n\n    public class LocalBinder extends Binder {\n        public ActivityCheckerService getService() {\n            return ActivityCheckerService.this;\n        }\n    }\n\n    @Override\n    public void onCreate() {\n        notificationManager = NotificationManagerCompat.from(getApplicationContext());\n        newsService = NewsService.getInstance();\n        handler = new Handler();\n        cb = new ServiceCallback<NotificationCounts>() {\n            @Override\n            public void onSuccess(final NotificationCounts result) {\n                try {\n                    if (result == null) return;\n                    final List<String> notification = getNotificationString(result);\n                    if (notification == null) return;\n                    showNotification(notification);\n                } finally {\n                    handler.postDelayed(runnable, DELAY_MILLIS);\n                }\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {}\n        };\n    }\n\n    @Override\n    public IBinder onBind(Intent intent) {\n        startChecking();\n        return binder;\n    }\n\n    @Override\n    public boolean onUnbind(final Intent intent) {\n        stopChecking();\n        return super.onUnbind(intent);\n    }\n\n    private void startChecking() {\n        handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);\n    }\n\n    private void stopChecking() {\n        handler.removeCallbacks(runnable);\n    }\n\n    private List<String> getNotificationString(final NotificationCounts result) {\n        final List<String> toReturn = new ArrayList<>(2);\n        final List<String> list = new ArrayList<>();\n        int count = 0;\n        if (result.getRelationships() != 0) {\n            list.add(getString(R.string.activity_count_relationship, result.getRelationships()));\n            count += result.getRelationships();\n        }\n        if (result.getRequests() != 0) {\n            list.add(getString(R.string.activity_count_requests, result.getRequests()));\n            count += result.getRequests();\n        }\n        if (result.getUsertags() != 0) {\n            list.add(getString(R.string.activity_count_usertags, result.getUsertags()));\n            count += result.getUsertags();\n        }\n        if (result.getPhotosOfYou() != 0) {\n            list.add(getString(R.string.activity_count_poy, result.getPhotosOfYou()));\n            count += result.getPhotosOfYou();\n        }\n        if (result.getComments() != 0) {\n            list.add(getString(R.string.activity_count_comments, result.getComments()));\n            count += result.getComments();\n        }\n        if (result.getCommentLikes() != 0) {\n            list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikes()));\n            count += result.getCommentLikes();\n        }\n        if (result.getLikes() != 0) {\n            list.add(getString(R.string.activity_count_likes, result.getLikes()));\n            count += result.getLikes();\n        }\n        if (list.isEmpty()) return null;\n        toReturn.add(TextUtils.join(\", \", list));\n        toReturn.add(getResources().getQuantityString(R.plurals.activity_count_total, count, count));\n        return toReturn;\n    }\n\n    private void showNotification(final List<String> notificationString) {\n        final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID)\n                .setCategory(NotificationCompat.CATEGORY_STATUS)\n                .setSmallIcon(R.drawable.ic_notif)\n                .setAutoCancel(true)\n                .setOnlyAlertOnce(true)\n                .setPriority(NotificationCompat.PRIORITY_DEFAULT)\n                .setContentTitle(notificationString.get(1))\n                .setContentText(notificationString.get(0))\n                .setStyle(new NotificationCompat.BigTextStyle().bigText(notificationString.get(0)))\n                .setContentIntent(getPendingIntent())\n                .build();\n        notificationManager.notify(Constants.ACTIVITY_NOTIFICATION_ID, notification);\n    }\n\n    @NonNull\n    private PendingIntent getPendingIntent() {\n        final Intent intent = new Intent(getApplicationContext(), MainActivity.class)\n                .setAction(Constants.ACTION_SHOW_ACTIVITY)\n                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);\n        return PendingIntent.getActivity(getApplicationContext(), Constants.SHOW_ACTIVITY_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/services/BootCompletedReceiver.java",
    "content": "package awais.instagrabber.services;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.TextUtils;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class BootCompletedReceiver extends BroadcastReceiver {\n    @Override\n    public void onReceive(final Context context, final Intent intent) {\n        if (!Objects.equals(intent.getAction(), \"android.intent.action.BOOT_COMPLETED\")) return;\n        final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);\n        if (!enabled) return;\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;\n        if (!isLoggedIn) return;\n        DMSyncAlarmReceiver.setAlarm(context);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/services/DMSyncAlarmReceiver.java",
    "content": "package awais.instagrabber.services;\n\nimport android.app.AlarmManager;\nimport android.app.PendingIntent;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalUnit;\n\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.utils.Constants;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class DMSyncAlarmReceiver extends BroadcastReceiver {\n    private static final String TAG = DMSyncAlarmReceiver.class.getSimpleName();\n\n    @Override\n    public void onReceive(final Context context, final Intent intent) {\n        final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);\n        if (!enabled) {\n            // If somehow the alarm was triggered even when auto refresh is disabled\n            cancelAlarm(context);\n            return;\n        }\n        try {\n            final Context applicationContext = context.getApplicationContext();\n            ContextCompat.startForegroundService(applicationContext, new Intent(applicationContext, DMSyncService.class));\n        } catch (Exception e) {\n            Log.e(TAG, \"onReceive: \", e);\n        }\n    }\n\n    public static void setAlarm(@NonNull final Context context) {\n        Log.d(TAG, \"setting DMSyncService Alarm\");\n        final AlarmManager alarmManager = getAlarmManager(context);\n        if (alarmManager == null) return;\n        final PendingIntent pendingIntent = getPendingIntent(context);\n        alarmManager.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(), getIntervalMillis(), pendingIntent);\n    }\n\n    public static void cancelAlarm(@NonNull final Context context) {\n        Log.d(TAG, \"cancelling DMSyncService Alarm\");\n        final AlarmManager alarmManager = getAlarmManager(context);\n        if (alarmManager == null) return;\n        final PendingIntent pendingIntent = getPendingIntent(context);\n        alarmManager.cancel(pendingIntent);\n    }\n\n    private static AlarmManager getAlarmManager(@NonNull final Context context) {\n        return (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);\n    }\n\n    private static PendingIntent getPendingIntent(@NonNull final Context context) {\n        final Context applicationContext = context.getApplicationContext();\n        final Intent intent = new Intent(applicationContext, DMSyncAlarmReceiver.class);\n        return PendingIntent.getBroadcast(applicationContext,\n                                          Constants.DM_SYNC_SERVICE_REQUEST_CODE,\n                                          intent,\n                                          PendingIntent.FLAG_UPDATE_CURRENT);\n    }\n\n    private static long getIntervalMillis() {\n        int amount = settingsHelper.getInteger(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER);\n        if (amount <= 0) {\n            amount = 30;\n        }\n        final String unit = settingsHelper.getString(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT);\n        final TemporalUnit temporalUnit;\n        switch (unit) {\n            case \"mins\":\n                temporalUnit = ChronoUnit.MINUTES;\n                break;\n            default:\n            case \"secs\":\n                temporalUnit = ChronoUnit.SECONDS;\n        }\n        return Duration.of(amount, temporalUnit).toMillis();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/services/DMSyncService.java",
    "content": "package awais.instagrabber.services;\n\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Resources;\nimport android.os.IBinder;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.core.app.NotificationCompat;\nimport androidx.lifecycle.LifecycleService;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMap;\n\nimport java.time.LocalDateTime;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.db.datasources.DMLastNotifiedDataSource;\nimport awais.instagrabber.db.entities.DMLastNotified;\nimport awais.instagrabber.db.repositories.DMLastNotifiedRepository;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.managers.DirectMessagesManager;\nimport awais.instagrabber.managers.InboxManager;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.repositories.responses.directmessages.DirectInbox;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.DMUtils;\nimport awais.instagrabber.utils.DateUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class DMSyncService extends LifecycleService {\n    private static final String TAG = DMSyncService.class.getSimpleName();\n\n    private InboxManager inboxManager;\n    private DMLastNotifiedRepository dmLastNotifiedRepository;\n    private Map<String, DMLastNotified> dmLastNotifiedMap;\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        startForeground(Constants.DM_CHECK_NOTIFICATION_ID, buildForegroundNotification());\n        Log.d(TAG, \"onCreate: Service created\");\n        final DirectMessagesManager directMessagesManager = DirectMessagesManager.INSTANCE;\n        inboxManager = directMessagesManager.getInboxManager();\n        final Context context = getApplicationContext();\n        if (context == null) return;\n        dmLastNotifiedRepository = DMLastNotifiedRepository.getInstance(DMLastNotifiedDataSource.getInstance(context));\n    }\n\n    private void parseUnread(@NonNull final DirectInbox directInbox) {\n        dmLastNotifiedRepository.getAllDMDmLastNotified(\n                CoroutineUtilsKt.getContinuation((result, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"parseUnread: \", throwable);\n                        dmLastNotifiedMap = Collections.emptyMap();\n                        parseUnreadActual(directInbox);\n                        return;\n                    }\n                    dmLastNotifiedMap = result != null\n                                        ? result.stream().collect(Collectors.toMap(DMLastNotified::getThreadId, Function.identity()))\n                                        : Collections.emptyMap();\n                    parseUnreadActual(directInbox);\n                }), Dispatchers.getIO())\n        );\n        // Log.d(TAG, \"inbox observer: \" + directInbox);\n    }\n\n    private void parseUnreadActual(@NonNull final DirectInbox directInbox) {\n        final List<DirectThread> threads = directInbox.getThreads();\n        final ImmutableMap.Builder<String, List<DirectItem>> unreadMessagesMapBuilder = ImmutableMap.builder();\n        if (threads == null) {\n            stopSelf();\n            return;\n        }\n        for (final DirectThread thread : threads) {\n            if (thread.getMuted()) continue;\n            final boolean read = DMUtils.isRead(thread);\n            if (read) continue;\n            final List<DirectItem> unreadMessages = getUnreadMessages(thread);\n            if (unreadMessages.isEmpty()) continue;\n            unreadMessagesMapBuilder.put(thread.getThreadId(), unreadMessages);\n        }\n        final Map<String, List<DirectItem>> unreadMessagesMap = unreadMessagesMapBuilder.build();\n        if (unreadMessagesMap.isEmpty()) {\n            stopSelf();\n            return;\n        }\n        showNotification(directInbox, unreadMessagesMap);\n        final LocalDateTime now = LocalDateTime.now();\n        // Update db\n        final ImmutableList.Builder<DMLastNotified> lastNotifiedListBuilder = ImmutableList.builder();\n        for (final Map.Entry<String, List<DirectItem>> unreadMessagesEntry : unreadMessagesMap.entrySet()) {\n            final List<DirectItem> unreadItems = unreadMessagesEntry.getValue();\n            final DirectItem latestItem = unreadItems.get(unreadItems.size() - 1);\n            lastNotifiedListBuilder.add(new DMLastNotified(0,\n                                                           unreadMessagesEntry.getKey(),\n                                                           latestItem.getDate(),\n                                                           now));\n        }\n        dmLastNotifiedRepository.insertOrUpdateDMLastNotified(\n                lastNotifiedListBuilder.build(),\n                CoroutineUtilsKt.getContinuation((unit, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    try {\n                        if (throwable != null) {\n                            Log.e(TAG, \"parseUnreadActual: \", throwable);\n                        }\n                    } finally {\n                        stopSelf();\n                    }\n                }), Dispatchers.getIO())\n        );\n    }\n\n    @NonNull\n    private List<DirectItem> getUnreadMessages(@NonNull final DirectThread thread) {\n        final List<DirectItem> items = thread.getItems();\n        if (items == null) return Collections.emptyList();\n        final DMLastNotified dmLastNotified = dmLastNotifiedMap.get(thread.getThreadId());\n        final long viewerId = thread.getViewerId();\n        final Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();\n        final ImmutableList.Builder<DirectItem> unreadListBuilder = ImmutableList.builder();\n        int count = 0;\n        for (final DirectItem item : items) {\n            if (item == null) continue;\n            if (item.getUserId() == viewerId) break; // Reached a message from the viewer, it is assumed the viewer has read the next messages\n            final boolean read = DMUtils.isRead(item, lastSeenAt, Collections.singletonList(viewerId));\n            if (read) break;\n            if (dmLastNotified != null && dmLastNotified.getLastNotifiedMsgTs() != null && item.getDate() != null) {\n                if (count == 0 && DateUtils.isBeforeOrEqual(item.getDate(), dmLastNotified.getLastNotifiedMsgTs())) {\n                    // The first unread item has been notified and hence all subsequent items can be ignored\n                    // since the items are in desc timestamp order\n                    break;\n                }\n            }\n            unreadListBuilder.add(item);\n            count++;\n            // Inbox style notification only allows 6 lines\n            if (count >= 6) break;\n        }\n        // Reversing, so that oldest messages are on top\n        return unreadListBuilder.build().reverse();\n    }\n\n    private void showNotification(final DirectInbox directInbox,\n                                  final Map<String, List<DirectItem>> unreadMessagesMap) {\n        final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n        if (notificationManager == null) return;\n        for (final Map.Entry<String, List<DirectItem>> unreadMessagesEntry : unreadMessagesMap.entrySet()) {\n            final Optional<DirectThread> directThreadOptional = getThread(directInbox, unreadMessagesEntry.getKey());\n            if (!directThreadOptional.isPresent()) continue;\n            final DirectThread thread = directThreadOptional.get();\n            final DirectItem firstDirectItem = thread.getFirstDirectItem();\n            if (firstDirectItem == null) continue;\n            final List<DirectItem> unreadMessages = unreadMessagesEntry.getValue();\n            final NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();\n            inboxStyle.setBigContentTitle(thread.getThreadTitle());\n            for (final DirectItem item : unreadMessages) {\n                inboxStyle.addLine(DMUtils.getMessageString(thread, getResources(), thread.getViewerId(), item));\n            }\n            final Notification notification = new NotificationCompat.Builder(this, Constants.DM_UNREAD_CHANNEL_ID)\n                    .setStyle(inboxStyle)\n                    .setSmallIcon(R.drawable.ic_round_mode_comment_24)\n                    .setContentTitle(thread.getThreadTitle())\n                    .setContentText(DMUtils.getMessageString(thread, getResources(), thread.getViewerId(), firstDirectItem))\n                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)\n                    .setDefaults(NotificationCompat.DEFAULT_ALL)\n                    .setGroup(Constants.GROUP_KEY_DM)\n                    .setAutoCancel(true)\n                    .setContentIntent(getThreadPendingIntent(thread.getThreadId(), thread.getThreadTitle()))\n                    .build();\n            notificationManager.notify(Constants.DM_UNREAD_PARENT_NOTIFICATION_ID, notification);\n        }\n    }\n\n    private Optional<DirectThread> getThread(@NonNull final DirectInbox directInbox, final String threadId) {\n        return directInbox.getThreads()\n                          .stream()\n                          .filter(thread -> Objects.equals(thread.getThreadId(), threadId))\n                          .findFirst();\n    }\n\n    @NonNull\n    private PendingIntent getThreadPendingIntent(final String threadId, final String threadTitle) {\n        final Intent intent = new Intent(getApplicationContext(), MainActivity.class)\n                .setAction(Constants.ACTION_SHOW_DM_THREAD)\n                .putExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID, threadId)\n                .putExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE, threadTitle)\n                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);\n        return PendingIntent.getActivity(getApplicationContext(), Constants.SHOW_DM_THREAD, intent, PendingIntent.FLAG_UPDATE_CURRENT);\n    }\n\n    @Override\n    public int onStartCommand(final Intent intent, final int flags, final int startId) {\n        super.onStartCommand(intent, flags, startId);\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;\n        if (!isLoggedIn) {\n            stopSelf();\n            return START_NOT_STICKY;\n        }\n        // Need to setup here if service was started by the boot completed receiver\n        CookieUtils.setupCookies(cookie);\n        final boolean notificationsEnabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS);\n        inboxManager.getInbox().observe(this, inboxResource -> {\n            if (!notificationsEnabled || inboxResource == null || inboxResource.status != Resource.Status.SUCCESS) {\n                stopSelf();\n                return;\n            }\n            final DirectInbox directInbox = inboxResource.data;\n            if (directInbox == null) {\n                stopSelf();\n                return;\n            }\n            parseUnread(directInbox);\n        });\n        Log.d(TAG, \"onStartCommand: refreshing inbox\");\n        // inboxManager.refresh();\n        return START_NOT_STICKY;\n    }\n\n    @Override\n    public IBinder onBind(@NonNull final Intent intent) {\n        super.onBind(intent);\n        return null;\n    }\n\n    private Notification buildForegroundNotification() {\n        final Resources resources = getResources();\n        return new NotificationCompat.Builder(this, Constants.SILENT_NOTIFICATIONS_CHANNEL_ID)\n                .setOngoing(true)\n                .setSound(null)\n                .setContentTitle(resources.getString(R.string.app_name))\n                .setContentText(resources.getString(R.string.checking_for_new_messages))\n                .setSmallIcon(R.mipmap.ic_launcher)\n                .setPriority(NotificationCompat.PRIORITY_LOW)\n                .setGroup(Constants.GROUP_KEY_SILENT_NOTIFICATIONS)\n                .build();\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/services/DeleteImageIntentService.java",
    "content": "package awais.instagrabber.services;\n\nimport android.app.IntentService;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.app.NotificationManagerCompat;\nimport androidx.documentfile.provider.DocumentFile;\n\nimport java.util.Random;\n\nimport awais.instagrabber.utils.TextUtils;\n\npublic class DeleteImageIntentService extends IntentService {\n    private final static String TAG = \"DeleteImageIntent\";\n    private static final int DELETE_IMAGE_SERVICE_REQUEST_CODE = 9010;\n    private static final Random random = new Random();\n\n    public static final String EXTRA_IMAGE_PATH = \"extra_image_path\";\n    public static final String EXTRA_NOTIFICATION_ID = \"extra_notification_id\";\n    public static final String DELETE_IMAGE_SERVICE = \"delete_image_service\";\n\n    public DeleteImageIntentService() {\n        super(DELETE_IMAGE_SERVICE);\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        startService(new Intent(this, DeleteImageIntentService.class));\n    }\n\n    @Override\n    protected void onHandleIntent(@Nullable Intent intent) {\n        if (intent != null && Intent.ACTION_DELETE.equals(intent.getAction()) && intent.hasExtra(EXTRA_IMAGE_PATH)) {\n            final String path = intent.getStringExtra(EXTRA_IMAGE_PATH);\n            if (TextUtils.isEmpty(path)) return;\n            // final File file = new File(path);\n            final Uri parse = Uri.parse(path);\n            if (parse == null) return;\n            final DocumentFile file = DocumentFile.fromSingleUri(getApplicationContext(), parse);\n            boolean deleted;\n            if (file.exists()) {\n                deleted = file.delete();\n                if (!deleted) {\n                    Log.w(TAG, \"onHandleIntent: file not deleted!\");\n                }\n            } else {\n                deleted = true;\n            }\n            if (deleted) {\n                final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);\n                NotificationManagerCompat.from(this).cancel(notificationId);\n            }\n        }\n    }\n\n    @NonNull\n    public static PendingIntent pendingIntent(@NonNull final Context context,\n                                              @NonNull final DocumentFile imagePath,\n                                              final int notificationId) {\n        final Intent intent = new Intent(context, DeleteImageIntentService.class);\n        intent.setAction(Intent.ACTION_DELETE);\n        intent.putExtra(EXTRA_IMAGE_PATH, imagePath.getUri().toString());\n        intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);\n        return PendingIntent.getService(context, random.nextInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/AppExecutors.kt",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage awais.instagrabber.utils\n\nimport android.os.Handler\nimport android.os.Looper\nimport com.google.common.util.concurrent.ListeningExecutorService\nimport com.google.common.util.concurrent.MoreExecutors\nimport java.util.concurrent.Executor\nimport java.util.concurrent.Executors\n\n/**\n * Global executor pools for the whole application.\n *\n *\n * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind\n * webservice requests).\n */\n// TODO replace with kotlin coroutines and Dispatchers\nobject AppExecutors {\n    val diskIO: Executor = Executors.newSingleThreadExecutor()\n    val networkIO: Executor = Executors.newFixedThreadPool(3)\n    val mainThread: MainThreadExecutor = MainThreadExecutor()\n    val tasksThread: ListeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10))\n\n    class MainThreadExecutor : Executor {\n        private val mainThreadHandler = Handler(Looper.getMainLooper())\n        override fun execute(command: Runnable) {\n            mainThreadHandler.post(command)\n        }\n\n        fun execute(command: Runnable?, delay: Int) {\n            mainThreadHandler.postDelayed(command!!, delay.toLong())\n        }\n\n        fun cancel(command: Runnable) {\n            mainThreadHandler.removeCallbacks(command)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/BarinstaDeepLinkHelper.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.net.Uri\nimport androidx.core.net.toUri\n\nprivate const val domain = \"barinsta\"\n\nfun getDirectThreadDeepLink(threadId: String, threadTitle: String, isPending: Boolean = false): Uri =\n    \"$domain://dm_thread/$threadId/$threadTitle?pending=${isPending}\".toUri()\n\nfun getProfileDeepLink(username: String): Uri = \"$domain://profile/$username\".toUri()\n\nfun getPostDeepLink(shortCode: String): Uri = \"$domain://post/$shortCode\".toUri()\n\nfun getLocationDeepLink(locationId: Long): Uri = \"$domain://location/$locationId\".toUri()\n\nfun getLocationDeepLink(locationId: String): Uri = \"$domain://location/$locationId\".toUri()\n\nfun getHashtagDeepLink(hashtag: String): Uri = \"$domain://hashtag/$hashtag\".toUri()\n\nfun getNotificationsDeepLink(type: String, targetId: Long = 0): Uri = \"$domain://notifications/$type?targetId=$targetId\".toUri()\n\nfun getSearchDeepLink(): Uri = \"$domain://search\".toUri()"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.ContentResolver\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.net.Uri\nimport android.util.Log\nimport android.util.LruCache\nimport androidx.core.util.Pair\nimport androidx.documentfile.provider.DocumentFile\nimport awais.instagrabber.utils.extensions.TAG\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.io.IOException\n\nobject BitmapUtils {\n    private val bitmapMemoryCache: LruCache<String, Bitmap>\n    const val THUMBNAIL_SIZE = 200f\n\n    @JvmStatic\n    fun addBitmapToMemoryCache(key: String, bitmap: Bitmap, force: Boolean) {\n        if (force || getBitmapFromMemCache(key) == null) {\n            bitmapMemoryCache.put(key, bitmap)\n        }\n    }\n\n    @JvmStatic\n    fun getBitmapFromMemCache(key: String): Bitmap? {\n        return bitmapMemoryCache[key]\n    }\n\n    @JvmStatic\n    suspend fun getThumbnail(context: Context, uri: Uri): BitmapResult? {\n        val key = uri.toString()\n        val cachedBitmap = getBitmapFromMemCache(key)\n        if (cachedBitmap != null) {\n            return BitmapResult(cachedBitmap, -1, -1)\n        }\n        return loadBitmap(context.contentResolver, uri, THUMBNAIL_SIZE, THUMBNAIL_SIZE, true)\n    }\n\n    /**\n     * Loads bitmap from given Uri\n     *\n     * @param contentResolver [ContentResolver] to resolve the uri\n     * @param uri             Uri from where Bitmap will be loaded\n     * @param reqWidth        Required width\n     * @param reqHeight       Required height\n     * @param addToCache      true if the loaded bitmap should be added to the mem cache\n     */\n    suspend fun loadBitmap(\n        contentResolver: ContentResolver?,\n        uri: Uri?,\n        reqWidth: Float,\n        reqHeight: Float,\n        addToCache: Boolean,\n    ): BitmapResult? = loadBitmap(contentResolver, uri, reqWidth, reqHeight, -1f, addToCache)\n\n    /**\n     * Loads bitmap from given Uri\n     *\n     * @param contentResolver [ContentResolver] to resolve the uri\n     * @param uri             Uri from where Bitmap will be loaded\n     * @param maxDimenSize    Max size of the largest side of the image\n     * @param addToCache      true if the loaded bitmap should be added to the mem cache\n     */\n    suspend fun loadBitmap(\n        contentResolver: ContentResolver?,\n        uri: Uri?,\n        maxDimenSize: Float,\n        addToCache: Boolean,\n    ): BitmapResult? = loadBitmap(contentResolver, uri, -1f, -1f, maxDimenSize, addToCache)\n\n    /**\n     * Loads bitmap from given Uri\n     *\n     * @param contentResolver [ContentResolver] to resolve the uri\n     * @param uri             Uri from where [Bitmap] will be loaded\n     * @param reqWidth        Required width (set to -1 if maxDimenSize provided)\n     * @param reqHeight       Required height (set to -1 if maxDimenSize provided)\n     * @param maxDimenSize    Max size of the largest side of the image (set to -1 if setting reqWidth and reqHeight)\n     * @param addToCache      true if the loaded bitmap should be added to the mem cache\n     */\n    private suspend fun loadBitmap(\n        contentResolver: ContentResolver?,\n        uri: Uri?,\n        reqWidth: Float,\n        reqHeight: Float,\n        maxDimenSize: Float,\n        addToCache: Boolean,\n    ): BitmapResult? =\n        if (contentResolver == null || uri == null) null else withContext(Dispatchers.IO) {\n            getBitmapResult(contentResolver,\n                uri,\n                reqWidth,\n                reqHeight,\n                maxDimenSize,\n                addToCache)\n        }\n\n    fun getBitmapResult(\n        contentResolver: ContentResolver,\n        uri: Uri,\n        reqWidth: Float,\n        reqHeight: Float,\n        maxDimenSize: Float,\n        addToCache: Boolean,\n    ): BitmapResult? {\n        var bitmapOptions: BitmapFactory.Options\n        var actualReqWidth = reqWidth\n        var actualReqHeight = reqHeight\n        try {\n            contentResolver.openInputStream(uri).use { input ->\n                val outBounds = BitmapFactory.Options()\n                outBounds.inJustDecodeBounds = true\n                outBounds.inPreferredConfig = Bitmap.Config.ARGB_8888\n                BitmapFactory.decodeStream(input, null, outBounds)\n                if (outBounds.outWidth == -1 || outBounds.outHeight == -1) return null\n                bitmapOptions = BitmapFactory.Options()\n                if (maxDimenSize > 0) {\n                    // Raw height and width of image\n                    val height = outBounds.outHeight\n                    val width = outBounds.outWidth\n                    val ratio = width.toFloat() / height\n                    if (height > width) {\n                        actualReqHeight = maxDimenSize\n                        actualReqWidth = actualReqHeight * ratio\n                    } else {\n                        actualReqWidth = maxDimenSize\n                        actualReqHeight = actualReqWidth / ratio\n                    }\n                }\n                bitmapOptions.inSampleSize = calculateInSampleSize(outBounds, actualReqWidth, actualReqHeight)\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"loadBitmap: \", e)\n            return null\n        }\n        try {\n            contentResolver.openInputStream(uri).use { input ->\n                bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888\n                val bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions)\n                if (addToCache && bitmap != null) {\n                    addBitmapToMemoryCache(uri.toString(), bitmap, true)\n                }\n                return BitmapResult(bitmap, actualReqWidth.toInt(), actualReqHeight.toInt())\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"loadBitmap: \", e)\n        }\n        return null\n    }\n\n    private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Float, reqHeight: Float): Int {\n        // Raw height and width of image\n        val height = options.outHeight\n        val width = options.outWidth\n        var inSampleSize = 1\n        if (height > reqHeight || width > reqWidth) {\n            val halfHeight = height / 2f\n            val halfWidth = width / 2f\n            // Calculate the largest inSampleSize value that is a power of 2 and keeps both\n            // height and width larger than the requested height and width.\n            while (halfHeight / inSampleSize >= reqHeight\n                   && halfWidth / inSampleSize >= reqWidth\n            ) {\n                inSampleSize *= 2\n            }\n        }\n        return inSampleSize\n    }\n\n    /**\n     * Decodes the bounds of an image from its Uri and returns a pair of the dimensions\n     *\n     * @param uri the Uri of the image\n     * @return dimensions of the image\n     */\n    @Throws(IOException::class)\n    fun decodeDimensions(\n        contentResolver: ContentResolver,\n        uri: Uri,\n    ): Pair<Int, Int>? {\n        val options = BitmapFactory.Options()\n        options.inJustDecodeBounds = true\n        contentResolver.openInputStream(uri).use { stream ->\n            BitmapFactory.decodeStream(stream, null, options)\n            return if (options.outWidth == -1 || options.outHeight == -1) null else Pair(options.outWidth, options.outHeight)\n        }\n    }\n\n    @Throws(IOException::class)\n    fun convertToJpegAndSaveToFile(contentResolver: ContentResolver, bitmap: Bitmap, file: DocumentFile?): DocumentFile? {\n        val tempFile = file ?: DownloadUtils.getTempFile(null, \"jpg\")\n        contentResolver.openOutputStream(tempFile!!.uri).use { output ->\n            val compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output)\n            if (!compressResult) {\n                throw RuntimeException(\"Compression failed!\")\n            }\n        }\n        return tempFile\n    }\n\n    @JvmStatic\n    @Throws(Exception::class)\n    fun convertToJpegAndSaveToUri(\n        context: Context,\n        bitmap: Bitmap,\n        uri: Uri,\n    ) {\n        context.contentResolver.openOutputStream(uri).use { output ->\n            val compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output)\n            if (!compressResult) {\n                throw RuntimeException(\"Compression failed!\")\n            }\n        }\n    }\n\n    class BitmapResult(var bitmap: Bitmap?, var width: Int, var height: Int)\n\n    init {\n        // Get max available VM memory, exceeding this amount will throw an\n        // OutOfMemory exception. Stored in kilobytes as LruCache takes an\n        // int in its constructor.\n        val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()\n        // Use 1/8th of the available memory for this memory cache.\n        val cacheSize: Int = maxMemory / 8\n        bitmapMemoryCache = object : LruCache<String, Bitmap>(cacheSize) {\n            override fun sizeOf(key: String, bitmap: Bitmap): Int {\n                // The cache size will be measured in kilobytes rather than\n                // number of items.\n                return bitmap.byteCount / 1024\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/CombinedDrawable.kt",
    "content": "/*\n * This is the source code of Telegram for Android v. 5.x.x.\n * It is licensed under GNU GPL v. 2 or later.\n * You should have received a copy of the license in this archive (see LICENSE).\n * <p>\n * Copyright Nikolai Kudashov, 2013-2018.\n */\npackage awais.instagrabber.utils\n\nimport android.graphics.Canvas\nimport android.graphics.ColorFilter\nimport android.graphics.PixelFormat\nimport android.graphics.drawable.Drawable\n\nclass CombinedDrawable : Drawable, Drawable.Callback {\n    val background: Drawable\n    val icon: Drawable?\n    private var left = 0\n    private var top = 0\n    private var iconWidth = 0\n    private var iconHeight = 0\n    private var backWidth = 0\n    private var backHeight = 0\n    private var offsetX = 0\n    private var offsetY = 0\n    private var fullSize = false\n\n    constructor(backgroundDrawable: Drawable, iconDrawable: Drawable?, leftOffset: Int, topOffset: Int) {\n        background = backgroundDrawable\n        icon = iconDrawable\n        left = leftOffset\n        top = topOffset\n        if (iconDrawable != null) {\n            iconDrawable.callback = this\n        }\n    }\n\n    constructor(backgroundDrawable: Drawable, iconDrawable: Drawable?) {\n        background = backgroundDrawable\n        icon = iconDrawable\n        if (iconDrawable != null) {\n            iconDrawable.callback = this\n        }\n    }\n\n    fun setIconSize(width: Int, height: Int) {\n        iconWidth = width\n        iconHeight = height\n    }\n\n    fun setCustomSize(width: Int, height: Int) {\n        backWidth = width\n        backHeight = height\n    }\n\n    fun setIconOffset(x: Int, y: Int) {\n        offsetX = x\n        offsetY = y\n    }\n\n    fun setFullsize(value: Boolean) {\n        fullSize = value\n    }\n\n    override fun setColorFilter(colorFilter: ColorFilter?) {\n        icon?.colorFilter = colorFilter\n    }\n\n    override fun isStateful(): Boolean {\n        return icon?.isStateful ?: false\n    }\n\n    override fun setState(stateSet: IntArray): Boolean {\n        icon?.state = stateSet\n        return true\n    }\n\n    override fun getState(): IntArray {\n        return icon?.state ?: super.getState()\n    }\n\n    override fun onStateChange(state: IntArray): Boolean {\n        return true\n    }\n\n    override fun jumpToCurrentState() {\n        icon?.jumpToCurrentState()\n    }\n\n    override fun getConstantState(): ConstantState? {\n        return icon?.constantState\n    }\n\n    override fun draw(canvas: Canvas) {\n        background.bounds = bounds\n        background.draw(canvas)\n        if (icon == null) return\n        if (fullSize) {\n            val bounds = bounds\n            if (left != 0) {\n                icon.setBounds(bounds.left + left, bounds.top + top, bounds.right - left, bounds.bottom - top)\n            } else {\n                icon.bounds = bounds\n            }\n        } else {\n            val x: Int\n            val y: Int\n            if (iconWidth != 0) {\n                x = bounds.centerX() - iconWidth / 2 + left + offsetX\n                y = bounds.centerY() - iconHeight / 2 + top + offsetY\n                icon.setBounds(x, y, x + iconWidth, y + iconHeight)\n            } else {\n                x = bounds.centerX() - icon.intrinsicWidth / 2 + left\n                y = bounds.centerY() - icon.intrinsicHeight / 2 + top\n                icon.setBounds(x, y, x + icon.intrinsicWidth, y + icon.intrinsicHeight)\n            }\n        }\n        icon.draw(canvas)\n    }\n\n    override fun setAlpha(alpha: Int) {\n        icon?.alpha = alpha\n        background.alpha = alpha\n    }\n\n    override fun getIntrinsicWidth(): Int {\n        return if (backWidth != 0) backWidth else background.intrinsicWidth\n    }\n\n    override fun getIntrinsicHeight(): Int {\n        return if (backHeight != 0) backHeight else background.intrinsicHeight\n    }\n\n    override fun getMinimumWidth(): Int {\n        return if (backWidth != 0) backWidth else background.minimumWidth\n    }\n\n    override fun getMinimumHeight(): Int {\n        return if (backHeight != 0) backHeight else background.minimumHeight\n    }\n\n    override fun getOpacity(): Int {\n        return icon?.opacity ?: PixelFormat.UNKNOWN\n    }\n\n    override fun invalidateDrawable(who: Drawable) {\n        invalidateSelf()\n    }\n\n    override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {\n        scheduleSelf(what, `when`)\n    }\n\n    override fun unscheduleDrawable(who: Drawable, what: Runnable) {\n        unscheduleSelf(what)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ConcurrencyHelpers.kt",
    "content": "package awais.instagrabber.utils\n\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.CoroutineStart.LAZY\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport java.util.concurrent.atomic.AtomicReference\n\n/**\n *\n * From https://gist.github.com/objcode/7ab4e7b1df8acd88696cb0ccecad16f7\n *\n * A helper class to execute tasks sequentially in coroutines.\n *\n * Calling [afterPrevious] will always ensure that all previously requested work completes prior to\n * calling the block passed. Any future calls to [afterPrevious] while the current block is running\n * will wait for the current block to complete before starting.\n */\nclass SingleRunner {\n    /**\n     * A coroutine mutex implements a lock that may only be taken by one coroutine at a time.\n     */\n    private val mutex = Mutex()\n\n    /**\n     * Ensure that the block will only be executed after all previous work has completed.\n     *\n     * When several coroutines call afterPrevious at the same time, they will queue up in the order\n     * that they call afterPrevious. Then, one coroutine will enter the block at a time.\n     *\n     * In the following example, only one save operation (user or song) will be executing at a time.\n     *\n     * ```\n     * class UserAndSongSaver {\n     *    val singleRunner = SingleRunner()\n     *\n     *    fun saveUser(user: User) {\n     *        singleRunner.afterPrevious { api.post(user) }\n     *    }\n     *\n     *    fun saveSong(song: Song) {\n     *        singleRunner.afterPrevious { api.post(song) }\n     *    }\n     * }\n     * ```\n     *\n     * @param block the code to run after previous work is complete.\n     */\n    suspend fun <T> afterPrevious(block: suspend () -> T): T {\n        // Before running the block, ensure that no other blocks are running by taking a lock on the\n        // mutex.\n\n        // The mutex will be released automatically when we return.\n\n        // If any other block were already running when we get here, it will wait for it to complete\n        // before entering the `withLock` block.\n        mutex.withLock {\n            return block()\n        }\n    }\n}\n\n/**\n * A controlled runner decides what to do when new tasks are run.\n *\n * By calling [joinPreviousOrRun], the new task will be discarded and the result of the previous task\n * will be returned. This is useful when you want to ensure that a network request to the same\n * resource does not flood.\n *\n * By calling [cancelPreviousThenRun], the old task will *always* be cancelled and then the new task will\n * be run. This is useful in situations where a new event implies that the previous work is no\n * longer relevant such as sorting or filtering a list.\n */\nclass ControlledRunner<T> {\n    /**\n     * The currently active task.\n     *\n     * This uses an atomic reference to ensure that it's safe to update activeTask on both\n     * Dispatchers.Default and Dispatchers.Main which will execute coroutines on multiple threads at\n     * the same time.\n     */\n    private val activeTask = AtomicReference<Deferred<T>?>(null)\n\n    /**\n     * Cancel all previous tasks before calling block.\n     *\n     * When several coroutines call cancelPreviousThenRun at the same time, only one will run and\n     * the others will be cancelled.\n     *\n     * In the following example, only one sort operation will execute and any previous sorts will be\n     * cancelled.\n     *\n     * ```\n     * class Products {\n     *    val controlledRunner = ControlledRunner<Product>()\n     *\n     *    fun sortAscending(): List<Product> {\n     *        return controlledRunner.cancelPreviousThenRun { dao.loadSortedAscending() }\n     *    }\n     *\n     *    fun sortDescending(): List<Product> {\n     *        return controlledRunner.cancelPreviousThenRun { dao.loadSortedDescending() }\n     *    }\n     * }\n     * ```\n     *\n     * @param block the code to run after previous work is cancelled.\n     * @return the result of block, if this call was not cancelled prior to returning.\n     */\n    suspend fun cancelPreviousThenRun(block: suspend () -> T): T {\n        // fast path: if we already know about an active task, just cancel it right away.\n        activeTask.get()?.cancelAndJoin()\n\n        return coroutineScope {\n            // Create a new coroutine, but don't start it until it's decided that this block should\n            // execute. In the code below, calling await() on newTask will cause this coroutine to\n            // start.\n            val newTask = async(start = LAZY) {\n                block()\n            }\n\n            // When newTask completes, ensure that it resets activeTask to null (if it was the\n            // current activeTask).\n            newTask.invokeOnCompletion {\n                activeTask.compareAndSet(newTask, null)\n            }\n\n            // Kotlin ensures that we only set result once since it's a val, even though it's set\n            // inside the while(true) loop.\n            val result: T\n\n            // Loop until we are sure that newTask is ready to execute (all previous tasks are\n            // cancelled)\n            while (true) {\n                if (!activeTask.compareAndSet(null, newTask)) {\n                    // some other task started before newTask got set to activeTask, so see if it's\n                    // still running when we call get() here. If so, we can cancel it.\n\n                    // we will always start the loop again to see if we can set activeTask before\n                    // starting newTask.\n                    activeTask.get()?.cancelAndJoin()\n                    // yield here to avoid a possible tight loop on a single threaded dispatcher\n                    yield()\n                } else {\n                    // happy path - we set activeTask so we are ready to run newTask\n                    result = newTask.await()\n                    break\n                }\n            }\n\n            // Kotlin ensures that the above loop always sets result exactly once, so we can return\n            // it here!\n            result\n        }\n    }\n\n    /**\n     * Don't run the new block if a previous block is running, instead wait for the previous block\n     * and return it's result.\n     *\n     * When several coroutines call jonPreviousOrRun at the same time, only one will run and\n     * the others will return the result from the winner.\n     *\n     * In the following example, only one network operation will execute at a time and any other\n     * requests will return the result from the \"in flight\" request.\n     *\n     * ```\n     * class Products {\n     *    val controlledRunner = ControlledRunner<Product>()\n     *\n     *    fun fetchProducts(): List<Product> {\n     *        return controlledRunner.joinPreviousOrRun {\n     *            val results = api.fetchProducts()\n     *            dao.insert(results)\n     *            results\n     *        }\n     *    }\n     * }\n     * ```\n     *\n     * @param block the code to run if and only if no other task is currently running\n     * @return the result of block, or if another task was running the result of that task instead.\n     */\n    suspend fun joinPreviousOrRun(block: suspend () -> T): T {\n        // fast path: if there's already an active task, just wait for it and return the result\n        activeTask.get()?.let {\n            return it.await()\n        }\n        return coroutineScope {\n            // Create a new coroutine, but don't start it until it's decided that this block should\n            // execute. In the code below, calling await() on newTask will cause this coroutine to\n            // start.\n            val newTask = async(start = LAZY) {\n                block()\n            }\n\n            newTask.invokeOnCompletion {\n                activeTask.compareAndSet(newTask, null)\n            }\n\n            // Kotlin ensures that we only set result once since it's a val, even though it's set\n            // inside the while(true) loop.\n            val result: T\n\n            // Loop until we figure out if we need to run newTask, or if there is a task that's\n            // already running we can join.\n            while (true) {\n                if (!activeTask.compareAndSet(null, newTask)) {\n                    // some other task started before newTask got set to activeTask, so see if it's\n                    // still running when we call get() here. There is a chance that it's already\n                    // been completed before the call to get, in which case we need to start the\n                    // loop over and try again.\n                    val currentTask = activeTask.get()\n                    if (currentTask != null) {\n                        // happy path - we found the other task so use that one instead of newTask\n                        newTask.cancel()\n                        result = currentTask.await()\n                        break\n                    } else {\n                        // retry path - the other task completed before we could get it, loop to try\n                        // setting activeTask again.\n\n                        // call yield here in case we're executing on a single threaded dispatcher\n                        // like Dispatchers.Main to allow other work to happen.\n                        yield()\n                    }\n                } else {\n                    // happy path - we were able to set activeTask, so start newTask and return its\n                    // result\n                    result = newTask.await()\n                    break\n                }\n            }\n\n            // Kotlin ensures that the above loop always sets result exactly once, so we can return\n            // it here!\n            result\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/Constants.kt",
    "content": "package awais.instagrabber.utils\n\nobject Constants {\n    const val CRASH_REPORT_EMAIL = \"barinsta@austinhuang.me\"\n\n    // int prefs, do not export\n    const val PREV_INSTALL_VERSION = \"prevVersion\"\n    const val BROWSER_UA_CODE = \"browser_ua_code\"\n    const val APP_UA_CODE = \"app_ua_code\"\n\n    // never Export\n    const val COOKIE = \"cookie\"\n\n    // deprecated: public static final String SHOW_QUICK_ACCESS_DIALOG = \"show_quick_dlg\";\n    const val DEVICE_UUID = \"device_uuid\"\n    const val BROWSER_UA = \"browser_ua\"\n    const val APP_UA = \"app_ua\"\n\n    //////////////////////// EXTRAS ////////////////////////\n    const val EXTRAS_USER = \"user\"\n    const val EXTRAS_HASHTAG = \"hashtag\"\n    const val EXTRAS_LOCATION = \"location\"\n    const val EXTRAS_USERNAME = \"username\"\n    const val EXTRAS_ID = \"id\"\n    const val EXTRAS_POST = \"post\"\n    const val EXTRAS_PROFILE = \"profile\"\n    const val EXTRAS_TYPE = \"type\"\n    const val EXTRAS_NAME = \"name\"\n    const val EXTRAS_STORIES = \"stories\"\n    const val EXTRAS_HIGHLIGHT = \"highlight\"\n    const val EXTRAS_INDEX = \"index\"\n    const val EXTRAS_THREAD_MODEL = \"threadModel\"\n    const val EXTRAS_FOLLOWERS = \"followers\"\n    const val EXTRAS_SHORTCODE = \"shortcode\"\n    const val EXTRAS_END_CURSOR = \"endCursor\"\n    const val FEED = \"feed\"\n    const val FEED_ORDER = \"feedOrder\"\n\n    // Notification ids\n    const val ACTIVITY_NOTIFICATION_ID = 10\n    const val DM_UNREAD_PARENT_NOTIFICATION_ID = 20\n    const val DM_CHECK_NOTIFICATION_ID = 11\n\n    // see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts\n    //    public static final String SUPPORTED_CAPABILITIES = \"[ { \\\"name\\\": \\\"SUPPORTED_SDK_VERSIONS\\\", \\\"value\\\":\" +\n    //            \" \\\"13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,\" +\n    //            \"32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,\" +\n    //            \"52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0,65.0,66.0\\\" }, { \\\"name\\\": \\\"FACE_TRACKER_VERSION\\\", \" +\n    //            \"\\\"value\\\": 12 }, { \\\"name\\\": \\\"segmentation\\\", \\\"value\\\": \\\"segmentation_enabled\\\" }, { \\\"name\\\": \\\"COMPRESSION\\\", \" +\n    //            \"\\\"value\\\": \\\"ETC2_COMPRESSION\\\" }, { \\\"name\\\": \\\"world_tracker\\\", \\\"value\\\": \\\"world_tracker_enabled\\\" }, { \\\"name\\\": \" +\n    //            \"\\\"gyroscope\\\", \\\"value\\\": \\\"gyroscope_enabled\\\" } ]\";\n    //    public static final String SIGNATURE_VERSION = \"4\";\n    //    public static final String SIGNATURE_KEY = \"9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc\";\n    const val BREADCRUMB_KEY = \"iN4\\$aGr0m\"\n    const val LOGIN_RESULT_CODE = 5000\n    const val SKIPPED_VERSION = \"skipped_version\"\n    const val DEFAULT_TAB = \"default_tab\"\n    const val PREF_DARK_THEME = \"dark_theme\"\n    const val PREF_LIGHT_THEME = \"light_theme\"\n    const val DEFAULT_HASH_TAG_PIC = \"https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png\"\n    const val SHARED_PREFERENCES_NAME = \"settings\"\n    const val PREF_POSTS_LAYOUT = \"posts_layout\"\n    const val PREF_PROFILE_POSTS_LAYOUT = \"profile_posts_layout\"\n    const val PREF_TOPIC_POSTS_LAYOUT = \"topic_posts_layout\"\n    const val PREF_HASHTAG_POSTS_LAYOUT = \"hashtag_posts_layout\"\n    const val PREF_LOCATION_POSTS_LAYOUT = \"location_posts_layout\"\n    const val PREF_LIKED_POSTS_LAYOUT = \"liked_posts_layout\"\n    const val PREF_TAGGED_POSTS_LAYOUT = \"tagged_posts_layout\"\n    const val PREF_SAVED_POSTS_LAYOUT = \"saved_posts_layout\"\n    const val PREF_EMOJI_VARIANTS = \"emoji_variants\"\n    const val PREF_REACTIONS = \"reactions\"\n    const val ACTIVITY_CHANNEL_ID = \"activity\"\n    const val ACTIVITY_CHANNEL_NAME = \"Activity\"\n    const val DOWNLOAD_CHANNEL_ID = \"download\"\n    const val DOWNLOAD_CHANNEL_NAME = \"Downloads\"\n    const val DM_UNREAD_CHANNEL_ID = \"dmUnread\"\n    const val DM_UNREAD_CHANNEL_NAME = \"Messages\"\n    const val SILENT_NOTIFICATIONS_CHANNEL_ID = \"silentNotifications\"\n    const val SILENT_NOTIFICATIONS_CHANNEL_NAME = \"Silent notifications\"\n    const val NOTIF_GROUP_NAME = \"awais.instagrabber.InstaNotif\"\n    const val GROUP_KEY_DM = \"awais.instagrabber.MESSAGES\"\n    const val GROUP_KEY_SILENT_NOTIFICATIONS = \"awais.instagrabber.SILENT_NOTIFICATIONS\"\n    const val SHOW_ACTIVITY_REQUEST_CODE = 1738\n    const val SHOW_DM_THREAD = 2000\n    const val DM_SYNC_SERVICE_REQUEST_CODE = 3000\n    const val GLOBAL_NETWORK_ERROR_DIALOG_REQUEST_CODE = 7777\n    const val ACTION_SHOW_ACTIVITY = \"show_activity\"\n    const val ACTION_SHOW_DM_THREAD = \"show_dm_thread\"\n    const val DM_THREAD_ACTION_EXTRA_THREAD_ID = \"thread_id\"\n    const val DM_THREAD_ACTION_EXTRA_THREAD_TITLE = \"thread_title\"\n    const val X_IG_APP_ID = \"936619743392459\"\n    const val EXTRA_INITIAL_URI = \"initial_uri\"\n    const val defaultDateTimeFormat = \"hh:mm:ss a 'on' dd-MM-yyyy\"\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/CookieUtils.kt",
    "content": "@file:JvmName(\"CookieUtils\")\n\npackage awais.instagrabber.utils\n\nimport android.content.Context\nimport android.util.Log\nimport android.webkit.CookieManager\nimport awais.instagrabber.db.repositories.AccountRepository\nimport java.net.CookiePolicy\nimport java.net.HttpCookie\nimport java.net.URI\nimport java.net.URISyntaxException\nimport java.util.regex.Pattern\n\nprivate const val TAG = \"CookieUtils\"\nprivate val COOKIE_MANAGER = CookieManager.getInstance()\n\n@JvmField\nval NET_COOKIE_MANAGER = java.net.CookieManager(null, CookiePolicy.ACCEPT_ALL)\n\nfun setupCookies(cookieRaw: String) {\n    val cookieStore = NET_COOKIE_MANAGER.cookieStore\n    if (cookieStore == null || TextUtils.isEmpty(cookieRaw)) {\n        return\n    }\n    if (cookieRaw == \"LOGOUT\") {\n        cookieStore.removeAll()\n        return\n    }\n    try {\n        val uri1 = URI(\"https://instagram.com\")\n        val uri2 = URI(\"https://instagram.com/\")\n        val uri3 = URI(\"https://i.instagram.com/\")\n        for (cookie in cookieRaw.split(\"; \")) {\n            val strings = cookie.split(\"=\", limit = 2)\n            val httpCookie = HttpCookie(strings[0].trim { it <= ' ' }, strings[1].trim { it <= ' ' })\n            httpCookie.domain = \".instagram.com\"\n            httpCookie.path = \"/\"\n            httpCookie.version = 0\n            cookieStore.add(uri1, httpCookie)\n            cookieStore.add(uri2, httpCookie)\n            cookieStore.add(uri3, httpCookie)\n        }\n    } catch (e: URISyntaxException) {\n        Log.e(TAG, \"\", e)\n    }\n}\n\nsuspend fun removeAllAccounts(context: Context) {\n    NET_COOKIE_MANAGER.cookieStore.removeAll()\n    AccountRepository.getInstance(context).deleteAllAccounts()\n}\n\nfun getUserIdFromCookie(cookies: String?): Long {\n    cookies ?: return 0\n    val dsUserId = getCookieValue(cookies, \"ds_user_id\") ?: return 0\n    try {\n        return dsUserId.toLong()\n    } catch (e: NumberFormatException) {\n        Log.e(TAG, \"getUserIdFromCookie: \", e)\n    }\n    return 0\n}\n\nfun getCsrfTokenFromCookie(cookies: String): String? {\n    return getCookieValue(cookies, \"csrftoken\")\n}\n\nprivate fun getCookieValue(cookies: String, name: String): String? {\n    val pattern = Pattern.compile(\"$name=(.+?);\")\n    val matcher = pattern.matcher(cookies)\n    return if (matcher.find()) {\n        matcher.group(1)\n    } else null\n}\n\nfun getCookie(webViewUrl: String?): String? {\n    val domains: List<String> = listOfNotNull(\n        if (!TextUtils.isEmpty(webViewUrl)) webViewUrl else null,\n        \"https://instagram.com\",\n        \"https://instagram.com/\",\n        \"http://instagram.com\",\n        \"http://instagram.com\",\n        \"https://www.instagram.com\",\n        \"https://www.instagram.com/\",\n        \"http://www.instagram.com\",\n        \"http://www.instagram.com/\",\n    )\n    return getLongestCookie(domains)\n}\n\nprivate fun getLongestCookie(domains: List<String>): String? {\n    var longestLength = 0\n    var longestCookie: String? = null\n    for (domain in domains) {\n        val cookie = COOKIE_MANAGER.getCookie(domain)\n        if (cookie != null) {\n            val cookieLength = cookie.length\n            if (cookieLength > longestLength) {\n                longestCookie = cookie\n                longestLength = cookieLength\n            }\n        }\n    }\n    return longestCookie\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/CoroutineUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.Dispatchers\nimport java.util.function.BiConsumer\nimport kotlin.coroutines.Continuation\nimport kotlin.coroutines.CoroutineContext\n\n@JvmOverloads\nfun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {\n    return object : Continuation<R> {\n        override val context: CoroutineContext\n            get() = dispatcher\n\n        override fun resumeWith(result: Result<R>) {\n            onFinished.accept(result.getOrNull(), result.exceptionOrNull())\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/CubicInterpolation.kt",
    "content": "package awais.instagrabber.utils\n\nimport java.util.*\n\nclass CubicInterpolation @JvmOverloads constructor(array: FloatArray, cubicTension: Int = 0) {\n    private val array: FloatArray\n    private val tangentFactor: Int\n    private val length: Int\n    private fun getTangent(k: Int): Float {\n        return tangentFactor * (getClippedInput(k + 1) - getClippedInput(k - 1)) / 2\n    }\n\n    fun interpolate(t: Float): Float {\n        val k = Math.floor(t.toDouble()).toInt()\n        val m = floatArrayOf(getTangent(k), getTangent(k + 1))\n        val p = floatArrayOf(getClippedInput(k), getClippedInput(k + 1))\n        val t1 = t - k\n        val t2 = t1 * t1\n        val t3 = t1 * t2\n        return (2 * t3 - 3 * t2 + 1) * p[0] + (t3 - 2 * t2 + t1) * m[0] + (-2 * t3 + 3 * t2) * p[1] + (t3 - t2) * m[1]\n    }\n\n    private fun getClippedInput(i: Int): Float {\n        return if (i >= 0 && i < length) {\n            array[i]\n        } else array[clipClamp(i, length)]\n    }\n\n    private fun clipClamp(i: Int, n: Int): Int {\n        return Math.max(0, Math.min(i, n - 1))\n    }\n\n    init {\n        this.array = Arrays.copyOf(array, array.size)\n        length = array.size\n        tangentFactor = 1 - Math.max(0, Math.min(1, cubicTension))\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/DMUtils.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.content.res.Resources;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.models.enums.DirectItemType;\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.ActionType;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread;\nimport awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;\nimport awais.instagrabber.repositories.responses.directmessages.RavenExpiringMediaActionSummary;\n\npublic final class DMUtils {\n    public static boolean isRead(@NonNull final DirectItem item,\n                                 @NonNull final Map<Long, DirectThreadLastSeenAt> lastSeenAt,\n                                 @NonNull final List<Long> userIdsToCheck) {\n        return lastSeenAt.entrySet()\n                         .stream()\n                         .filter(entry -> userIdsToCheck.contains(entry.getKey()))\n                         .anyMatch(entry -> {\n                             final DirectThreadLastSeenAt threadLastSeenAt = entry.getValue();\n                             if (threadLastSeenAt == null) return false;\n                             final String userLastSeenTsString = threadLastSeenAt.getTimestamp();\n                             if (userLastSeenTsString == null) return false;\n                             final long userTs = Long.parseLong(userLastSeenTsString);\n                             final long itemTs = item.getTimestamp();\n                             return userTs >= itemTs;\n                         });\n    }\n\n    public static boolean isRead(@NonNull final DirectThread thread) {\n        final boolean read;\n        // if (thread.getDirectStory() != null) {\n        //     return false;\n        // }\n        final DirectItem item = thread.getFirstDirectItem();\n        final long viewerId = thread.getViewerId();\n        if (item != null && item.getUserId() == viewerId) {\n            // if last item was sent by user, then it is read (even though we have auto read unchecked?)\n            read = true;\n        } else {\n            final Map<Long, DirectThreadLastSeenAt> lastSeenAtMap = thread.getLastSeenAt();\n            read = item != null && isRead(item, lastSeenAtMap, Collections.singletonList(viewerId));\n        }\n        return read;\n    }\n\n    public static String getMessageString(@NonNull final DirectThread thread,\n                                          final Resources resources,\n                                          final long viewerId,\n                                          final DirectItem item) {\n        final long senderId = item.getUserId();\n        final DirectItemType itemType = item.getItemType();\n        String subtitle = null;\n        final String username = getUsername(thread.getUsers(), senderId, viewerId, resources);\n        String message = \"\";\n        if (itemType == null) {\n            message = resources.getString(R.string.dms_inbox_raven_message_unknown);\n        } else {\n            switch (itemType) {\n                case TEXT:\n                    message = item.getText();\n                    break;\n                case LIKE:\n                    message = item.getLike();\n                    break;\n                case LINK:\n                    message = item.getLink().getText();\n                    break;\n                case PLACEHOLDER:\n                    message = item.getPlaceholder().getMessage();\n                    break;\n                case MEDIA_SHARE:\n                    final Media mediaShare = item.getMediaShare();\n                    User mediaShareUser = null;\n                    if (mediaShare != null) {\n                        mediaShareUser = mediaShare.getUser();\n                    }\n                    subtitle = resources.getString(R.string.dms_inbox_shared_post,\n                                                   username != null ? username : \"\",\n                                                   mediaShareUser == null ? \"\" : mediaShareUser.getUsername());\n                    break;\n                case ANIMATED_MEDIA:\n                    final DirectItemAnimatedMedia animatedMedia = item.getAnimatedMedia();\n                    subtitle = resources.getString(animatedMedia.isSticker() ? R.string.dms_inbox_shared_sticker\n                                                                             : R.string.dms_inbox_shared_gif,\n                                                   username != null ? username : \"\");\n                    break;\n                case PROFILE:\n                    subtitle = resources\n                            .getString(R.string.dms_inbox_shared_profile, username != null ? username : \"\", item.getProfile().getUsername());\n                    break;\n                case LOCATION:\n                    subtitle = resources\n                            .getString(R.string.dms_inbox_shared_location, username != null ? username : \"\", item.getLocation().getName());\n                    break;\n                case MEDIA: {\n                    final MediaItemType mediaType = item.getMedia().getType();\n                    subtitle = getMediaSpecificSubtitle(username, resources, mediaType);\n                    break;\n                }\n                case STORY_SHARE: {\n                    final String reelType = item.getStoryShare().getReelType();\n                    if (reelType == null) {\n                        subtitle = item.getStoryShare().getTitle();\n                    } else {\n                        final int format = reelType.equals(\"highlight_reel\")\n                                           ? R.string.dms_inbox_shared_highlight\n                                           : R.string.dms_inbox_shared_story;\n                        final Media media = item.getStoryShare().getMedia();\n                        User storyShareMediaUser = null;\n                        if (media != null) {\n                            storyShareMediaUser = media.getUser();\n                        }\n                        subtitle = resources.getString(format,\n                                                       username != null ? username : \"\",\n                                                       storyShareMediaUser == null ? \"\" : storyShareMediaUser.getUsername());\n                    }\n                    break;\n                }\n                case VOICE_MEDIA:\n                    subtitle = resources.getString(R.string.dms_inbox_shared_voice, username != null ? username : \"\");\n                    break;\n                case ACTION_LOG:\n                    subtitle = item.getActionLog().getDescription();\n                    break;\n                case VIDEO_CALL_EVENT:\n                    subtitle = item.getVideoCallEvent().getDescription();\n                    break;\n                case CLIP:\n                    final Media clip = item.getClip().getClip();\n                    User clipUser = null;\n                    if (clip != null) {\n                        clipUser = clip.getUser();\n                    }\n                    subtitle = resources.getString(R.string.dms_inbox_shared_clip,\n                                                   username != null ? username : \"\",\n                                                   clipUser == null ? \"\" : clipUser.getUsername());\n                    break;\n                case FELIX_SHARE:\n                    final Media video = item.getFelixShare().getVideo();\n                    User felixShareVideoUser = null;\n                    if (video != null) {\n                        felixShareVideoUser = video.getUser();\n                    }\n                    subtitle = resources.getString(R.string.dms_inbox_shared_igtv,\n                                                   username != null ? username : \"\",\n                                                   felixShareVideoUser == null ? \"\" : felixShareVideoUser.getUsername());\n                    break;\n                case RAVEN_MEDIA:\n                    subtitle = getRavenMediaSubtitle(item, resources, username);\n                    break;\n                case REEL_SHARE:\n                    final DirectItemReelShare reelShare = item.getReelShare();\n                    if (reelShare == null) {\n                        subtitle = \"\";\n                        break;\n                    }\n                    final String reelType = reelShare.getType();\n                    switch (reelType) {\n                        case \"reply\":\n                            if (viewerId == item.getUserId()) {\n                                subtitle = resources.getString(R.string.dms_inbox_replied_story_outgoing, reelShare.getText());\n                            } else {\n                                subtitle = resources\n                                        .getString(R.string.dms_inbox_replied_story_incoming, username != null ? username : \"\", reelShare.getText());\n                            }\n                            break;\n                        case \"mention\":\n                            if (viewerId == item.getUserId()) {\n                                // You mentioned the other person\n                                final long mentionedUserId = item.getReelShare().getMentionedUserId();\n                                final String otherUsername = getUsername(thread.getUsers(), mentionedUserId, viewerId, resources);\n                                subtitle = resources.getString(R.string.dms_inbox_mentioned_story_outgoing, otherUsername);\n                            } else {\n                                // They mentioned you\n                                subtitle = resources.getString(R.string.dms_inbox_mentioned_story_incoming, username != null ? username : \"\");\n                            }\n                            break;\n                        case \"reaction\":\n                            if (viewerId == item.getUserId()) {\n                                subtitle = resources.getString(R.string.dms_inbox_reacted_story_outgoing, reelShare.getText());\n                            } else {\n                                subtitle = resources\n                                        .getString(R.string.dms_inbox_reacted_story_incoming, username != null ? username : \"\", reelShare.getText());\n                            }\n                            break;\n                        default:\n                            subtitle = \"\";\n                            break;\n                    }\n                    break;\n                case XMA:\n                    subtitle = resources.getString(R.string.dms_inbox_shared_sticker, username != null ? username : \"\");\n                    break;\n                default:\n                    message = resources.getString(R.string.dms_inbox_raven_message_unknown);\n            }\n        }\n        if (subtitle == null) {\n            if (thread.isGroup() || (!thread.isGroup() && senderId == viewerId)) {\n                subtitle = String.format(\"%s: %s\", username != null ? username : \"\", message);\n            } else {\n                subtitle = message;\n            }\n        }\n        return subtitle;\n    }\n\n    public static String getUsername(final List<User> users,\n                                     final long userId,\n                                     final long viewerId,\n                                     final Resources resources) {\n        if (userId == viewerId) {\n            return resources.getString(R.string.you);\n        }\n        final Optional<User> senderOptional = users.stream()\n                                                   .filter(Objects::nonNull)\n                                                   .filter(user -> user.getPk() == userId)\n                                                   .findFirst();\n        return senderOptional.map(user -> {\n            // return full name for fb users\n            final String username = user.getUsername();\n            if (TextUtils.isEmpty(username)) {\n                return user.getFullName();\n            }\n            return username;\n        }).orElse(null);\n    }\n\n    public static String getMediaSpecificSubtitle(final String username, final Resources resources, final MediaItemType mediaType) {\n        final String userSharedAnImage = resources.getString(R.string.dms_inbox_shared_image, username != null ? username : \"\");\n        final String userSharedAVideo = resources.getString(R.string.dms_inbox_shared_video, username != null ? username : \"\");\n        final String userSentAMessage = resources.getString(R.string.dms_inbox_shared_message, username != null ? username : \"\");\n        String subtitle;\n        switch (mediaType) {\n            case MEDIA_TYPE_IMAGE:\n                subtitle = userSharedAnImage;\n                break;\n            case MEDIA_TYPE_VIDEO:\n                subtitle = userSharedAVideo;\n                break;\n            default:\n                subtitle = userSentAMessage;\n                break;\n        }\n        return subtitle;\n    }\n\n    private static String getRavenMediaSubtitle(final DirectItem item,\n                                                final Resources resources,\n                                                final String username) {\n        String subtitle = \"↗ \";\n        final DirectItemVisualMedia visualMedia = item.getVisualMedia();\n        final RavenExpiringMediaActionSummary summary = visualMedia.getExpiringMediaActionSummary();\n        if (summary != null) {\n            final ActionType expiringMediaType = summary.getType();\n            int textRes = 0;\n            switch (expiringMediaType) {\n                case DELIVERED:\n                    textRes = R.string.dms_inbox_raven_media_delivered;\n                    break;\n                case SENT:\n                    textRes = R.string.dms_inbox_raven_media_sent;\n                    break;\n                case OPENED:\n                    textRes = R.string.dms_inbox_raven_media_opened;\n                    break;\n                case REPLAYED:\n                    textRes = R.string.dms_inbox_raven_media_replayed;\n                    break;\n                case SENDING:\n                    textRes = R.string.dms_inbox_raven_media_sending;\n                    break;\n                case BLOCKED:\n                    textRes = R.string.dms_inbox_raven_media_blocked;\n                    break;\n                case SUGGESTED:\n                    textRes = R.string.dms_inbox_raven_media_suggested;\n                    break;\n                case SCREENSHOT:\n                    textRes = R.string.dms_inbox_raven_media_screenshot;\n                    break;\n                case CANNOT_DELIVER:\n                    textRes = R.string.dms_inbox_raven_media_cant_deliver;\n                    break;\n            }\n            if (textRes > 0) {\n                subtitle += resources.getString(textRes);\n            }\n            return subtitle;\n        }\n        final MediaItemType mediaType = visualMedia.getMedia().getType();\n        subtitle = getMediaSpecificSubtitle(username, resources, mediaType);\n        return subtitle;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/DateUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.util.Log\nimport awais.instagrabber.utils.extensions.TAG\nimport java.time.LocalDateTime\nimport java.time.format.DateTimeFormatter\nimport java.util.*\n\nobject DateUtils {\n    val timezoneOffset: Int\n        get() {\n            val calendar = Calendar.getInstance(Locale.getDefault())\n            return -(calendar[Calendar.ZONE_OFFSET] + calendar[Calendar.DST_OFFSET]) / (60 * 1000)\n        }\n\n    @JvmStatic\n    fun isBeforeOrEqual(localDateTime: LocalDateTime, comparedTo: LocalDateTime): Boolean {\n        return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo)\n    }\n\n    @JvmStatic\n    fun checkFormatterValid(datetimeParser: DateTimeFormatter): Boolean = try {\n        LocalDateTime.now().format(datetimeParser)\n        true\n    } catch (e: Exception) {\n        Log.e(TAG, \"checkFormatterValid: \", e)\n        false\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/Debouncer.java",
    "content": "package awais.instagrabber.utils;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\n// TODO replace with kotlinx-coroutines debounce\npublic class Debouncer<T> {\n    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);\n    private final ConcurrentHashMap<T, TimerTask> delayedMap = new ConcurrentHashMap<>();\n    private final ConcurrentHashMap<T, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();\n    private final Callback<T> callback;\n    private final int interval;\n\n    public Debouncer(Callback<T> c, int interval) {\n        this.callback = c;\n        this.interval = interval;\n    }\n\n    public void call(T key) {\n        TimerTask task = new TimerTask(key);\n\n        TimerTask prev;\n        do {\n            prev = delayedMap.putIfAbsent(key, task);\n            if (prev == null) {\n                final ScheduledFuture<?> future = scheduler.schedule(task, interval, TimeUnit.MILLISECONDS);\n                futureMap.put(key, future);\n            }\n        } while (prev != null && !prev.extend()); // Exit only if new task was added to map, or existing task was extended successfully\n    }\n\n    public void terminate() {\n        scheduler.shutdownNow();\n    }\n\n    public void cancel(final T key) {\n        delayedMap.remove(key);\n        final ScheduledFuture<?> future = futureMap.get(key);\n        if (future != null) {\n            future.cancel(true);\n        }\n    }\n\n    // The task that wakes up when the wait time elapses\n    private class TimerTask implements Runnable {\n        private final T key;\n        private long dueTime;\n        private final Object lock = new Object();\n\n        public TimerTask(T key) {\n            this.key = key;\n            extend();\n        }\n\n        public boolean extend() {\n            synchronized (lock) {\n                if (dueTime < 0) // Task has been shutdown\n                    return false;\n                dueTime = System.currentTimeMillis() + interval;\n                return true;\n            }\n        }\n\n        public void run() {\n            synchronized (lock) {\n                long remaining = dueTime - System.currentTimeMillis();\n                if (remaining > 0) { // Re-schedule task\n                    scheduler.schedule(this, remaining, TimeUnit.MILLISECONDS);\n                } else { // Mark as terminated and invoke callback\n                    dueTime = -1;\n                    try {\n                        callback.call(key);\n                    } catch (Exception e) {\n                        callback.onError(e);\n                    } finally {\n                        delayedMap.remove(key);\n                    }\n                }\n            }\n        }\n    }\n\n    public interface Callback<T> {\n        void call(T key);\n\n        void onError(Throwable t);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/DeepLinkParser.kt",
    "content": "package awais.instagrabber.utils\n\nimport java.util.regex.Pattern\n\nobject DeepLinkParser {\n    private val TYPE_PATTERN_MAP: Map<DeepLink.Type, DeepLinkPattern> = mapOf(\n        DeepLink.Type.USER to DeepLinkPattern(\"instagram://user?username=\"),\n    )\n\n    @JvmStatic\n    fun parse(text: String): DeepLink? {\n        for ((key, value) in TYPE_PATTERN_MAP) {\n            if (text.startsWith(value.patternText)) {\n                return DeepLink(key, value.pattern.matcher(text).replaceAll(\"\"))\n            }\n        }\n        return null\n    }\n\n    data class DeepLinkPattern(val patternText: String) {\n        val pattern: Pattern = Pattern.compile(patternText, Pattern.LITERAL)\n    }\n\n    data class DeepLink(val type: Type, val value: String) {\n        enum class Type {\n            USER\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/DirectItemFactory.kt",
    "content": "@file:JvmName(\"DirectItemFactory\")\n\npackage awais.instagrabber.utils\n\nimport android.net.Uri\nimport awais.instagrabber.models.enums.DirectItemType\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.repositories.responses.*\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia\nimport awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif\nimport java.util.*\n\nfun createText(\n    userId: Long,\n    clientContext: String?,\n    text: String?,\n    repliedToMessage: DirectItem?\n): DirectItem {\n    return DirectItem(\n        itemId = UUID.randomUUID().toString(),\n        userId = userId,\n        timestamp = System.currentTimeMillis() * 1000,\n        itemType = DirectItemType.TEXT,\n        text = text,\n        clientContext = clientContext,\n        repliedToMessage = repliedToMessage,\n    )\n}\n\nfun createImageOrVideo(\n    userId: Long,\n    clientContext: String?,\n    uri: Uri,\n    width: Int,\n    height: Int,\n    isVideo: Boolean\n): DirectItem {\n    val imageVersions2 = ImageVersions2(listOf(MediaCandidate(width, height, uri.toString())))\n    var videoVersions: List<MediaCandidate>? = null\n    if (isVideo) {\n        val videoVersion = MediaCandidate(\n            width,\n            height,\n            uri.toString()\n        )\n        videoVersions = listOf(videoVersion)\n    }\n    val media = Media(\n        id = UUID.randomUUID().toString(),\n        imageVersions2 = imageVersions2,\n        originalWidth = width,\n        originalHeight = height,\n        mediaType = if (isVideo) MediaItemType.MEDIA_TYPE_VIDEO.id else MediaItemType.MEDIA_TYPE_IMAGE.id,\n        videoVersions = videoVersions,\n    )\n    return DirectItem(\n        itemId = UUID.randomUUID().toString(),\n        userId = userId,\n        timestamp = System.currentTimeMillis() * 1000,\n        itemType = DirectItemType.MEDIA,\n        clientContext = clientContext,\n        media = media,\n    )\n}\n\nfun createVoice(\n    userId: Long,\n    clientContext: String?,\n    uri: Uri,\n    duration: Long,\n    waveform: List<Float>?,\n    samplingFreq: Int\n): DirectItem {\n    val audio = Audio(\n        uri.toString(),\n        duration,\n        waveform,\n        samplingFreq,\n        0\n    )\n    val media = Media(\n        id = UUID.randomUUID().toString(),\n        mediaType = MediaItemType.MEDIA_TYPE_VOICE.id,\n        audio = audio,\n    )\n    val voiceMedia = DirectItemVoiceMedia(\n        media,\n        0,\n        \"permanent\"\n    )\n    return DirectItem(\n        itemId = UUID.randomUUID().toString(),\n        userId = userId,\n        timestamp = System.currentTimeMillis() * 1000,\n        itemType = DirectItemType.VOICE_MEDIA,\n        clientContext = clientContext,\n        media = media,\n        voiceMedia = voiceMedia,\n    )\n}\n\nfun createAnimatedMedia(\n    userId: Long,\n    clientContext: String?,\n    giphyGif: GiphyGif\n): DirectItem {\n    val animatedImages = AnimatedMediaImages(giphyGif.images.fixedHeight)\n    val animateMedia = DirectItemAnimatedMedia(\n        giphyGif.id,\n        animatedImages,\n        false,\n        giphyGif.isSticker\n    )\n    return DirectItem(\n        itemId = UUID.randomUUID().toString(),\n        userId = userId,\n        timestamp = System.currentTimeMillis() * 1000,\n        itemType = DirectItemType.ANIMATED_MEDIA,\n        clientContext = clientContext,\n        animatedMedia = animateMedia,\n    )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.UriPermission\nimport android.net.Uri\nimport android.provider.DocumentsContract\nimport android.util.Log\nimport android.view.MenuItem\nimport android.view.View\nimport androidx.appcompat.view.ContextThemeWrapper\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.documentfile.provider.DocumentFile\nimport androidx.work.*\nimport awais.instagrabber.R\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.stories.StoryMedia\nimport awais.instagrabber.utils.AppExecutors.tasksThread\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.workers.DownloadWorker\nimport com.google.gson.Gson\nimport java.io.BufferedWriter\nimport java.io.IOException\nimport java.io.OutputStreamWriter\nimport java.util.*\nimport java.util.regex.Pattern\nimport kotlin.math.abs\n\n\nobject DownloadUtils {\n    private val TAG = DownloadUtils::class.java.simpleName\n\n    // private static final String DIR_BARINSTA = \"Barinsta\";\n    private const val DIR_DOWNLOADS = \"Downloads\"\n    private const val DIR_CAMERA = \"Camera\"\n    private const val DIR_EDIT = \"Edit\"\n    private const val DIR_RECORDINGS = \"Sent Recordings\"\n    private const val DIR_TEMP = \"Temp\"\n    private const val DIR_BACKUPS = \"Backups\"\n    private const val MIME_DIR = DocumentsContract.Document.MIME_TYPE_DIR\n    private val dirMap: MutableMap<String, DocumentFile?> = mutableMapOf()\n    private var root: DocumentFile? = null\n    @JvmStatic\n    @Throws(ReselectDocumentTreeException::class)\n    fun init(\n        context: Context,\n        barinstaDirUri: String?\n    ) {\n        if (isEmpty(barinstaDirUri)) {\n            throw ReselectDocumentTreeException(\"folder path is null or empty\")\n        }\n        val uri = Uri.parse(barinstaDirUri)\n        if (!barinstaDirUri!!.startsWith(\"content://com.android.externalstorage.documents\")) {\n            throw ReselectDocumentTreeException(uri)\n        }\n        val existingPermissions = context.contentResolver.persistedUriPermissions\n        if (existingPermissions.isEmpty()) {\n            throw ReselectDocumentTreeException(uri)\n        }\n        val anyMatch = existingPermissions.stream()\n            .anyMatch { uriPermission: UriPermission -> uriPermission.uri == uri }\n        if (!anyMatch) {\n            throw ReselectDocumentTreeException(uri)\n        }\n        root = DocumentFile.fromTreeUri(context, uri)\n        if (root == null || !root!!.exists() || root!!.lastModified() == 0L) {\n            root = null\n            throw ReselectDocumentTreeException(uri)\n        }\n        Utils.settingsHelper.putString(PreferenceKeys.PREF_BARINSTA_DIR_URI, uri.toString())\n        // set up directories\n        val dirKeys = mapOf(\n            DIR_DOWNLOADS to MIME_DIR,\n            DIR_CAMERA to MIME_DIR,\n            DIR_EDIT to MIME_DIR,\n            DIR_RECORDINGS to MIME_DIR,\n            DIR_TEMP to MIME_DIR,\n            DIR_BACKUPS to MIME_DIR\n        )\n        dirMap.putAll(checkFiles(context, root, dirKeys, true))\n    }\n\n    fun destroy() {\n        root = null\n        dirMap.clear()\n    }\n\n    fun checkFiles(context: Context,\n                   parent: DocumentFile?,\n                   queries: Map<String, String>, // <file name, mime type>\n                   create: Boolean\n    ): Map<String, DocumentFile?> {\n        // first we'll find existing ones\n        val result: MutableMap<String, DocumentFile?> = mutableMapOf()\n        if (root == null || parent == null || !parent.isDirectory) return result.toMap()\n        val docId = DocumentsContract.getDocumentId(parent.uri)\n        val docUri = DocumentsContract.buildChildDocumentsUriUsingTree(root!!.uri, docId)\n        val docCursor = context.contentResolver.query(\n            docUri, arrayOf(\n                DocumentsContract.Document.COLUMN_DISPLAY_NAME,\n                DocumentsContract.Document.COLUMN_DOCUMENT_ID,\n                DocumentsContract.Document.COLUMN_MIME_TYPE\n            ), null, null, null\n        )\n        if (docCursor == null) return result.toMap()\n        while (docCursor.moveToNext()) {\n            val q = queries.get(docCursor.getString(0))\n            if (q == null || !docCursor.getString(2).equals(q)) continue\n            val fileUri = DocumentsContract.buildDocumentUriUsingTree(parent.uri, docCursor.getString(1))\n            val dir = if (q.equals(MIME_DIR)) DocumentFile.fromTreeUri(context, fileUri)\n                      else DocumentFile.fromSingleUri(context, fileUri)\n            result.put(docCursor.getString(0), dir)\n            if (result.size >= queries.size) break\n        }\n        docCursor.close()\n        // next we'll create inexistent ones, if necessary\n        if (create) {\n            for (k in queries) {\n                if (result.get(k.key) == null) {\n                    result.put(k.key, if (MIME_DIR.equals(k.value)) parent.createDirectory(k.key)\n                                      else parent.createFile(k.value, k.key))\n                }\n            }\n        }\n        return result.toMap()\n    }\n\n    fun getRootDir(dir: String): DocumentFile? {\n        if (root == null) return null\n        return dirMap.get(dir)\n    }\n\n    @JvmStatic\n    val downloadDir: DocumentFile?\n        get() = getRootDir(DIR_DOWNLOADS)\n\n    @JvmStatic\n    val cameraDir: DocumentFile?\n        get() = getRootDir(DIR_CAMERA)\n\n    @JvmStatic\n    fun getImageEditDir(sessionId: String?, context: Context): DocumentFile? {\n        val editRoot = getRootDir(DIR_EDIT)\n        if (sessionId == null) return editRoot\n        return checkFiles(context,\n                          editRoot,\n                          mapOf(sessionId to MIME_DIR),\n                          true).get(sessionId)\n    }\n\n    @JvmStatic\n    val recordingsDir: DocumentFile?\n        get() = getRootDir(DIR_RECORDINGS)\n\n    @JvmStatic\n    val backupsDir: DocumentFile?\n        get() = getRootDir(DIR_BACKUPS)\n\n    private fun getDownloadDir(\n        context: Context,\n        username: String?,\n        shouldCreate: Boolean\n    ): DocumentFile? {\n        if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || username.isNullOrEmpty())\n            return downloadDir\n        return checkFiles(context,\n                          downloadDir,\n                          mapOf(username to MIME_DIR),\n                          shouldCreate).get(username)\n    }\n\n    private val tempDir: DocumentFile?\n        get() = getRootDir(DIR_TEMP)\n\n    private fun getDownloadSavePaths(\n        postId: String?,\n        displayUrl: String?\n    ): Pair<String, String> {\n        return getDownloadFileName(postId, \"\", displayUrl, \"\")\n    }\n\n    private fun getDownloadSavePaths(\n        postId: String?,\n        displayUrl: String,\n        username: String\n    ): Pair<String, String> {\n        return getDownloadFileName(postId, \"\", displayUrl, username)\n    }\n\n    private fun getDownloadChildSavePaths(\n        postId: String?,\n        childPosition: Int,\n        url: String?,\n        username: String\n    ): Pair<String, String> {\n        val sliderPostfix = \"_slide_$childPosition\"\n        return getDownloadFileName(postId, sliderPostfix, url, username)\n    }\n\n    private fun getDownloadFileName(\n        postId: String?,\n        sliderPostfix: String,\n        displayUrl: String?,\n        username: String\n    ): Pair<String, String> {\n        val extension = getFileExtensionFromUrl(displayUrl)\n        val usernamePrepend = if (isEmpty(username)) \"\" else username + \"_\"\n        val fileName = usernamePrepend + postId + sliderPostfix + extension\n        val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension)\n        return Pair(fileName, mimeType!!)\n    }\n\n    fun getTempFile(fileName: String?, extension: String): DocumentFile? {\n        val dir = tempDir\n        var name = fileName\n        if (isEmpty(name)) {\n            name = UUID.randomUUID().toString()\n        }\n        var mimeType: String? = \"application/octet-stream\"\n        if (!isEmpty(extension)) {\n            name += \".$extension\"\n            val mimeType1 = Utils.mimeTypeMap.getMimeTypeFromExtension(extension)\n            if (mimeType1 != null) {\n                mimeType = mimeType1\n            }\n        }\n        var file = dir!!.findFile(name!!)\n        if (file == null) {\n            file = dir.createFile(mimeType!!, name)\n        }\n        return file\n    }\n\n    /**\n     * Copied from [MimeTypeMap.getFileExtensionFromUrl])\n     *\n     *\n     * Returns the file extension or an empty string if there is no\n     * extension. This method is a convenience method for obtaining the\n     * extension of a url and has undefined results for other Strings.\n     *\n     * @param url URL\n     * @return The file extension of the given url.\n     */\n    @JvmStatic\n    fun getFileExtensionFromUrl(url: String?): String {\n        var url = url\n        if (!isEmpty(url)) {\n            val fragment = url!!.lastIndexOf('#')\n            if (fragment > 0) {\n                url = url.substring(0, fragment)\n            }\n            val query = url.lastIndexOf('?')\n            if (query > 0) {\n                url = url.substring(0, query)\n            }\n            val filenamePos = url.lastIndexOf('/')\n            val filename = if (0 <= filenamePos) url.substring(filenamePos + 1) else url\n\n            // if the filename contains special characters, we don't\n            // consider it valid for our matching purposes:\n            if (!filename.isEmpty() &&\n                Pattern.matches(\"[a-zA-Z_0-9.\\\\-()%]+\", filename)\n            ) {\n                val dotPos = filename.lastIndexOf('.')\n                if (0 <= dotPos) {\n                    return filename.substring(dotPos + 1)\n                }\n            }\n        }\n        return \"\"\n    }\n\n    @JvmStatic\n    fun checkDownloaded(media: Media, context: Context): List<Boolean> {\n        val checkList: MutableList<Boolean> = LinkedList()\n        val user = media.user\n        var username = \"username\"\n        if (user != null) {\n            username = user.username\n        }\n        val userFolder = getDownloadDir(context, username, false)\n        if (userFolder == null) return checkList\n        when (media.type) {\n            MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> {\n                val url =\n                    if (media.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(\n                        media\n                    ) else ResponseBodyUtils.getImageUrl(media)\n                val fileName = getDownloadSavePaths(media.code, url)\n                val fileNameWithUser = getDownloadSavePaths(media.code, url, username)\n                val files = checkFiles(context, userFolder, mapOf(fileName, fileNameWithUser), false)\n                checkList.add(files.size > 0)\n            }\n            MediaItemType.MEDIA_TYPE_SLIDER -> {\n                val sliderItems = media.carouselMedia\n                val fileNames: MutableMap<String, String> = mutableMapOf()\n                val filePairs: MutableMap<String, String> = mutableMapOf()\n                var i = 0\n                while (i < sliderItems!!.size) {\n                    val child = sliderItems[i]\n                    val url =\n                        if (child.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(\n                            child\n                        ) else ResponseBodyUtils.getImageUrl(child)\n                    val fileName = getDownloadChildSavePaths(media.code, i+1, url, \"\")\n                    val fileNameWithUser = getDownloadChildSavePaths(media.code, i+1, url, username)\n                    fileNames.put(fileName.first, fileName.second)\n                    fileNames.put(fileNameWithUser.first, fileNameWithUser.second)\n                    filePairs.put(fileName.first, fileNameWithUser.first)\n                    i++\n                }\n                val files = checkFiles(context, userFolder, fileNames, false)\n                for (p in filePairs) {\n                    checkList.add(files.get(p.key) != null || files.get(p.value) != null)\n                }\n            }\n            else -> {\n            }\n        }\n        return checkList\n    }\n\n    @JvmStatic\n    fun showDownloadDialog(\n        context: Context,\n        feedModel: Media,\n        childPosition: Int,\n        popupLocation: View?\n    ) {\n        if (childPosition == -1 || popupLocation == null) {\n            download(context, feedModel)\n            return\n        }\n        val themeWrapper = ContextThemeWrapper(context, R.style.popupMenuStyle)\n        val popupMenu = PopupMenu(themeWrapper, popupLocation)\n        val menu = popupMenu.menu\n        menu.add(0, R.id.download_current, 0, R.string.post_viewer_download_current)\n        menu.add(0, R.id.download_all, 1, R.string.post_viewer_download_album)\n        popupMenu.setOnMenuItemClickListener { item: MenuItem ->\n            val itemId = item.itemId\n            if (itemId == R.id.download_current) {\n                download(context, feedModel, childPosition)\n            } else if (itemId == R.id.download_all) {\n                download(context, feedModel)\n            }\n            false\n        }\n        popupMenu.show()\n    }\n\n    @JvmStatic\n    fun download(\n        context: Context,\n        storyModel: StoryMedia\n    ) {\n        val downloadDir = getDownloadDir(context, storyModel.user?.username, true) ?: return\n        val url =\n            if (storyModel.type == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl(storyModel)\n            else ResponseBodyUtils.getImageUrl(storyModel)\n        val extension = getFileExtensionFromUrl(url)\n        val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension)\n        val baseFileName = storyModel.id + \"_\" + storyModel.takenAt + extension\n        val usernamePrepend =\n            if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME)\n                && storyModel.user?.username != null\n            ) storyModel.user.username + \"_\" else \"\"\n        val fileName = usernamePrepend + baseFileName\n        var saveFile = checkFiles(context, downloadDir, mapOf(fileName to mimeType!!), true).get(fileName)\n        download(context, url, saveFile)\n    }\n\n    @JvmOverloads\n    @JvmStatic\n    fun download(\n        context: Context,\n        feedModel: Media,\n        position: Int = -1\n    ) {\n        download(context, listOf(feedModel), position)\n    }\n\n    // this must be used for bulk download, but ONLY bulk download\n    @JvmStatic\n    fun download(\n        context: Context,\n        feedModels: List<Media>\n    ) {\n        val builder = NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)\n            .setCategory(NotificationCompat.CATEGORY_PROGRESS)\n            .setSmallIcon(R.drawable.ic_download)\n            .setOngoing(true)\n            .setProgress(1, 0, true)\n            .setAutoCancel(false)\n            .setOnlyAlertOnce(true)\n            .setContentTitle(context.getString(R.string.downloader_preparing))\n        val notification = builder.build()\n        val nid = abs(UUID.randomUUID().hashCode())\n        val nManager = NotificationManagerCompat.from(context.applicationContext)\n        nManager.notify(nid, notification)\n        tasksThread.execute {\n            download(context, feedModels, -1)\n            nManager.cancel(nid)\n        }\n    }\n\n    private fun download(\n        context: Context,\n        feedModels: List<Media>,\n        childPositionIfSingle: Int\n    ) {\n        val map: MutableMap<String, Pair<String, String>> = HashMap()\n        val fileMap: MutableMap<String, DocumentFile?> = HashMap()\n        for (media in feedModels) {\n            val mediaUser = media.user\n            val username = mediaUser?.username ?: \"\"\n            val dir = getDownloadDir(context, username, true)\n            when (media.type) {\n                MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> {\n                    val url = getUrlOfType(media)\n                    var fileName = media.id\n                    if (mediaUser != null && isEmpty(media.code)) {\n                        fileName = mediaUser.username + \"_\" + fileName\n                    }\n                    if (!isEmpty(media.code)) {\n                        fileName = media.code\n                        if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) {\n                            fileName = mediaUser.username + \"_\" + fileName\n                        }\n                    }\n                    val pair = getDownloadSavePaths(fileName, url)\n                    map[url!!] = pair\n                }\n                MediaItemType.MEDIA_TYPE_VOICE -> {\n                    val url = getUrlOfType(media)\n                    var fileName = media.id\n                    if (mediaUser != null) {\n                        fileName = mediaUser.username + \"_\" + fileName\n                    }\n                    val pair = getDownloadSavePaths(fileName, url)\n                    map[url!!] = pair\n                }\n                MediaItemType.MEDIA_TYPE_SLIDER -> {\n                    val sliderItems = media.carouselMedia\n                    var i = 0\n                    while (i < sliderItems!!.size) {\n                        if (childPositionIfSingle >= 0 && feedModels.size == 1 && i != childPositionIfSingle) {\n                            i++\n                            continue\n                        }\n                        val child = sliderItems[i]\n                        val url = getUrlOfType(child)\n                        val usernamePrepend =\n                            if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) mediaUser.username else \"\"\n                        val pair = getDownloadChildSavePaths(media.code, i + 1, url, usernamePrepend)\n                        map[url!!] = pair\n                        i++\n                    }\n                }\n            }\n            fileMap.putAll(checkFiles(context, dir, map.values.toMap(), true))\n        }\n        if (map.isEmpty() || fileMap.isEmpty()) return\n        val resultMap: MutableMap<String, DocumentFile?> = mutableMapOf()\n        map.mapValuesTo(resultMap) { fileMap.get(it.value.first) }\n        download(context, resultMap)\n    }\n\n    private fun getUrlOfType(media: Media): String? {\n        when (media.type) {\n            MediaItemType.MEDIA_TYPE_IMAGE -> {\n                return ResponseBodyUtils.getImageUrl(media)\n            }\n            MediaItemType.MEDIA_TYPE_VIDEO -> {\n                val videoVersions = media.videoVersions\n                var url: String? = null\n                if (videoVersions != null && !videoVersions.isEmpty()) {\n                    url = videoVersions[0].url\n                }\n                return url\n            }\n            MediaItemType.MEDIA_TYPE_VOICE -> {\n                val audio = media.audio\n                var url: String? = null\n                if (audio != null) {\n                    url = audio.audioSrc\n                }\n                return url\n            }\n        }\n        return null\n    }\n\n    @JvmStatic\n    fun download(\n        context: Context?,\n        url: String?,\n        filePath: DocumentFile?\n    ) {\n        if (context == null || filePath == null) return\n        download(context, Collections.singletonMap(url!!, filePath))\n    }\n\n    private fun download(context: Context?, urlFilePathMap: Map<String, DocumentFile?>) {\n        if (context == null) return\n        val constraints = Constraints.Builder()\n            .setRequiredNetworkType(NetworkType.CONNECTED)\n            .build()\n        val request = DownloadWorker.DownloadRequest.builder()\n            .setUrlToFilePathMap(urlFilePathMap)\n            .build()\n        val requestJson = Gson().toJson(request)\n        val tempFile = getTempFile(null, \"json\")\n        if (tempFile == null) {\n            Log.e(TAG, \"download: temp file is null\")\n            return\n        }\n        val uri = tempFile.uri\n        val contentResolver = context.contentResolver ?: return\n        try {\n            BufferedWriter(OutputStreamWriter(contentResolver.openOutputStream(uri))).use { writer ->\n                writer.write(\n                    requestJson\n                )\n            }\n        } catch (e: IOException) {\n            Log.e(TAG, \"download: Error writing request to file\", e)\n            tempFile.delete()\n            return\n        }\n        val downloadWorkRequest: WorkRequest =\n            OneTimeWorkRequest.Builder(DownloadWorker::class.java)\n                .setInputData(\n                    Data.Builder()\n                        .putString(\n                            DownloadWorker.KEY_DOWNLOAD_REQUEST_JSON,\n                            tempFile.uri.toString()\n                        )\n                        .build()\n                )\n                .setConstraints(constraints)\n                .addTag(\"download\")\n                .build()\n        WorkManager.getInstance(context)\n            .enqueue(downloadWorkRequest)\n    }\n\n    @JvmStatic\n    fun getRootDirUri(): Uri? {\n        return if (root != null) root!!.uri else null\n    }\n\n    class ReselectDocumentTreeException : Exception {\n        val initialUri: Uri?\n\n        constructor() {\n            initialUri = null\n        }\n\n        constructor(message: String?) : super(message) {\n            initialUri = null\n        }\n\n        constructor(initialUri: Uri?) {\n            this.initialUri = initialUri\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/Event.kt",
    "content": "package awais.instagrabber.utils\n\n/**\n * Used as a wrapper for data that is exposed via a LiveData that represents an event.\n */\nopen class Event<out T>(private val content: T) {\n\n    var hasBeenHandled = false\n        private set // Allow external read but not write\n\n    /**\n     * Returns the content and prevents its use again.\n     */\n    fun getContentIfNotHandled(): T? {\n        return if (hasBeenHandled) {\n            null\n        } else {\n            hasBeenHandled = true\n            content\n        }\n    }\n\n    /**\n     * Returns the content, even if it's already been handled.\n     */\n    fun peekContent(): T = content\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ExoplayerUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.Context\nimport com.google.android.exoplayer2.database.ExoDatabaseProvider\nimport com.google.android.exoplayer2.source.DefaultMediaSourceFactory\nimport com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory\nimport com.google.android.exoplayer2.upstream.cache.CacheDataSource\nimport com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor\nimport com.google.android.exoplayer2.upstream.cache.SimpleCache\n\nobject ExoplayerUtils {\n    private const val MAX_CACHE_BYTES: Long = 1048576\n    private val cacheEvictor = LeastRecentlyUsedCacheEvictor(MAX_CACHE_BYTES)\n    fun getCachedMediaSourceFactory(context: Context): DefaultMediaSourceFactory {\n        val exoDatabaseProvider = ExoDatabaseProvider(context)\n        val simpleCache = SimpleCache(context.cacheDir, cacheEvictor, exoDatabaseProvider)\n        val cacheDataSourceFactory = CacheDataSource.Factory()\n                .setCache(simpleCache)\n                .setUpstreamDataSourceFactory(DefaultHttpDataSourceFactory())\n        return DefaultMediaSourceFactory(cacheDataSourceFactory)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.net.Uri;\nimport android.util.Base64;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.widget.Toast;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.util.concurrent.FutureCallback;\nimport com.google.common.util.concurrent.Futures;\nimport com.google.common.util.concurrent.ListenableFuture;\nimport com.google.common.util.concurrent.SettableFuture;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport awais.instagrabber.BuildConfig;\nimport awais.instagrabber.db.entities.Account;\nimport awais.instagrabber.db.entities.Favorite;\nimport awais.instagrabber.db.repositories.AccountRepository;\nimport awais.instagrabber.db.repositories.FavoriteRepository;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.models.enums.FavoriteType;\nimport awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\n//import awaisomereport.LogCollector.LogFile;\n//import static awais.instagrabber.utils.Utils.logCollector;\n\npublic final class ExportImportUtils {\n    private static final String TAG = \"ExportImportUtils\";\n\n    public static final int FLAG_COOKIES = 1;\n    public static final int FLAG_FAVORITES = 1 << 1;\n    public static final int FLAG_SETTINGS = 1 << 2;\n\n    public static void importData(@NonNull final Context context,\n                                  @ExportImportFlags final int flags,\n                                  @NonNull final Uri uri,\n                                  final String password,\n                                  final FetchListener<Boolean> fetchListener) throws IncorrectPasswordException {\n        try (final InputStream stream = context.getContentResolver().openInputStream(uri)) {\n            if (stream == null) return;\n            final int configType = stream.read();\n            final StringBuilder builder = new StringBuilder();\n            int c;\n            while ((c = stream.read()) != -1) {\n                builder.append((char) c);\n            }\n            if (configType == 'A') {\n                // password\n                if (TextUtils.isEmpty(password)) return;\n                try {\n                    final byte[] passwordBytes = password.getBytes();\n                    final byte[] bytes = new byte[32];\n                    System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));\n                    importJson(context,\n                               new String(PasswordUtils.dec(builder.toString(), bytes)),\n                               flags,\n                               fetchListener);\n                } catch (final IncorrectPasswordException e) {\n                    throw e;\n                } catch (final Exception e) {\n                    if (fetchListener != null) fetchListener.onResult(false);\n                    if (BuildConfig.DEBUG) Log.e(TAG, \"Error importing backup\", e);\n                }\n            } else if (configType == 'Z') {\n                importJson(context,\n                           new String(Base64.decode(builder.toString(), Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)),\n                           flags,\n                           fetchListener);\n\n            } else {\n                Toast.makeText(context, \"File is corrupted!\", Toast.LENGTH_LONG).show();\n                if (fetchListener != null) fetchListener.onResult(false);\n            }\n        } catch (IncorrectPasswordException e) {\n            // separately handle incorrect password\n            throw e;\n        } catch (final Exception e) {\n            if (fetchListener != null) fetchListener.onResult(false);\n            if (BuildConfig.DEBUG) Log.e(TAG, \"\", e);\n        }\n    }\n\n    private static void importJson(final Context context,\n                                   @NonNull final String json,\n                                   @ExportImportFlags final int flags,\n                                   final FetchListener<Boolean> fetchListener) {\n        try {\n            final JSONObject jsonObject = new JSONObject(json);\n            if ((flags & FLAG_SETTINGS) == FLAG_SETTINGS && jsonObject.has(\"settings\")) {\n                importSettings(jsonObject);\n            }\n            if ((flags & FLAG_COOKIES) == FLAG_COOKIES && jsonObject.has(\"cookies\")) {\n                importAccounts(context, jsonObject);\n            }\n            if ((flags & FLAG_FAVORITES) == FLAG_FAVORITES && jsonObject.has(\"favs\")) {\n                importFavorites(context, jsonObject);\n            }\n            if (fetchListener != null) fetchListener.onResult(true);\n        } catch (final Exception e) {\n            if (fetchListener != null) fetchListener.onResult(false);\n            if (BuildConfig.DEBUG) Log.e(TAG, \"\", e);\n        }\n    }\n\n    private static void importFavorites(final Context context, final JSONObject jsonObject) throws JSONException {\n        final JSONArray favs = jsonObject.getJSONArray(\"favs\");\n        for (int i = 0; i < favs.length(); i++) {\n            final JSONObject favsObject = favs.getJSONObject(i);\n            final String queryText = favsObject.optString(\"q\");\n            if (TextUtils.isEmpty(queryText)) continue;\n            final Pair<FavoriteType, String> favoriteTypeQueryPair;\n            String query = null;\n            FavoriteType favoriteType = null;\n            if (queryText.contains(\"@\")\n                    || queryText.contains(\"#\")\n                    || queryText.contains(\"/\")) {\n                favoriteTypeQueryPair = Utils.migrateOldFavQuery(queryText);\n                if (favoriteTypeQueryPair != null) {\n                    query = favoriteTypeQueryPair.second;\n                    favoriteType = favoriteTypeQueryPair.first;\n                }\n            } else {\n                query = queryText;\n                favoriteType = FavoriteType.valueOf(favsObject.optString(\"type\"));\n            }\n            if (query == null || favoriteType == null) {\n                continue;\n            }\n            final long epochMillis = favsObject.getLong(\"d\");\n            final Favorite favorite = new Favorite(\n                    0,\n                    query,\n                    favoriteType,\n                    favsObject.optString(\"s\"),\n                    favoriteType == FavoriteType.USER ? favsObject.optString(\"pic_url\") : null,\n                    LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), ZoneId.systemDefault())\n            );\n            // Log.d(TAG, \"importJson: favoriteModel: \" + favoriteModel);\n            final FavoriteRepository favRepo = FavoriteRepository.Companion.getInstance(context);\n            favRepo.getFavorite(\n                    query,\n                    favoriteType,\n                    CoroutineUtilsKt.getContinuation((favorite1, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        if (throwable != null) {\n                            Log.e(TAG, \"importFavorites: \", throwable);\n                            return;\n                        }\n                        if (favorite1 == null) {\n                            favRepo.insertOrUpdateFavorite(favorite, CoroutineUtilsKt.getContinuation((unit, throwable1) -> {}, Dispatchers.getIO()));\n                        }\n                        // local has priority since it's more frequently updated\n                    }), Dispatchers.getIO())\n            );\n        }\n    }\n\n    private static void importAccounts(final Context context,\n                                       final JSONObject jsonObject) {\n        final List<Account> accounts = new ArrayList<>();\n        try {\n            final JSONArray cookies = jsonObject.getJSONArray(\"cookies\");\n            for (int i = 0; i < cookies.length(); i++) {\n                final JSONObject cookieObject = cookies.getJSONObject(i);\n                final Account account = new Account(\n                        -1,\n                        cookieObject.optString(\"i\"),\n                        cookieObject.optString(\"u\"),\n                        cookieObject.optString(\"c\"),\n                        cookieObject.optString(\"full_name\"),\n                        cookieObject.optString(\"profile_pic\")\n                );\n                if (!account.isValid()) continue;\n                accounts.add(account);\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"importAccounts: Error parsing json\", e);\n            return;\n        }\n        AccountRepository.Companion\n                .getInstance(context)\n                .insertOrUpdateAccounts(accounts, CoroutineUtilsKt.getContinuation((unit, throwable) -> {}, Dispatchers.getIO()));\n    }\n\n    private static void importSettings(final JSONObject jsonObject) {\n        try {\n            final JSONObject objSettings = jsonObject.getJSONObject(\"settings\");\n            final Iterator<String> keys = objSettings.keys();\n            while (keys.hasNext()) {\n                final String key = keys.next();\n                final Object val = objSettings.opt(key);\n                // Log.d(TAG, \"importJson: key: \" + key + \", val: \" + val);\n                if (val instanceof String) {\n                    settingsHelper.putString(key, (String) val);\n                } else if (val instanceof Integer) {\n                    settingsHelper.putInteger(key, (int) val);\n                } else if (val instanceof Boolean) {\n                    settingsHelper.putBoolean(key, (boolean) val);\n                }\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"importSettings error\", e);\n        }\n    }\n\n    public static boolean isEncrypted(@NonNull final Context context,\n                                      @NonNull final Uri uri) {\n        try (final InputStream stream = context.getContentResolver().openInputStream(uri)) {\n            if (stream == null) return false;\n            final int configType = stream.read();\n            if (configType == 'A') {\n                return true;\n            }\n        } catch (final Exception e) {\n            Log.e(TAG, \"isEncrypted\", e);\n        }\n        return false;\n    }\n\n    public static void exportData(@NonNull final Context context,\n                                  @ExportImportFlags final int flags,\n                                  @NonNull final Uri uri,\n                                  final String password,\n                                  final FetchListener<Boolean> fetchListener) {\n        getExportString(flags, context, exportString -> {\n            if (TextUtils.isEmpty(exportString)) return;\n            final boolean isPass = !TextUtils.isEmpty(password);\n            byte[] exportBytes = null;\n            if (isPass) {\n                final byte[] passwordBytes = password.getBytes();\n                final byte[] bytes = new byte[32];\n                System.arraycopy(passwordBytes, 0, bytes, 0, Math.min(passwordBytes.length, 32));\n                try {\n                    exportBytes = PasswordUtils.enc(exportString, bytes);\n                } catch (final Exception e) {\n                    if (fetchListener != null) fetchListener.onResult(false);\n                    if (BuildConfig.DEBUG) Log.e(TAG, \"\", e);\n                }\n            } else {\n                exportBytes = Base64.encode(exportString.getBytes(), Base64.DEFAULT | Base64.NO_WRAP | Base64.NO_PADDING);\n            }\n            if (exportBytes != null && exportBytes.length > 1) {\n                try (final OutputStream stream = context.getContentResolver().openOutputStream(uri)) {\n                    if (stream == null) return;\n                    stream.write(isPass ? 'A' : 'Z');\n                    stream.write(exportBytes);\n                    if (fetchListener != null) fetchListener.onResult(true);\n                } catch (Exception e) {\n                    if (fetchListener != null) fetchListener.onResult(false);\n                    if (BuildConfig.DEBUG) Log.e(TAG, \"\", e);\n                }\n                return;\n            }\n            if (fetchListener != null) {\n                fetchListener.onResult(false);\n            }\n        });\n\n    }\n\n    private static void getExportString(@ExportImportFlags final int flags,\n                                        @NonNull final Context context,\n                                        final OnExportStringCreatedCallback callback) {\n        if (callback == null) return;\n        try {\n            final ImmutableList.Builder<ListenableFuture<?>> futures = ImmutableList.builder();\n            futures.add((flags & FLAG_SETTINGS) == FLAG_SETTINGS\n                        ? getSettings(context)\n                        : Futures.immediateFuture(null));\n            futures.add((flags & FLAG_COOKIES) == FLAG_COOKIES\n                        ? getCookies(context)\n                        : Futures.immediateFuture(null));\n            futures.add((flags & FLAG_FAVORITES) == FLAG_FAVORITES\n                        ? getFavorites(context)\n                        : Futures.immediateFuture(null));\n            //noinspection UnstableApiUsage\n            final ListenableFuture<List<Object>> allFutures = Futures.allAsList(futures.build());\n            Futures.addCallback(allFutures, new FutureCallback<List<Object>>() {\n                @Override\n                public void onSuccess(final List<Object> result) {\n                    final JSONObject jsonObject = new JSONObject();\n                    if (result == null) {\n                        callback.onCreated(jsonObject.toString());\n                        return;\n                    }\n                    try {\n                        final JSONObject settings = (JSONObject) result.get(0);\n                        if (settings != null) {\n                            jsonObject.put(\"settings\", settings);\n                        }\n                    } catch (Exception e) {\n                        Log.e(TAG, \"error getting settings: \", e);\n                    }\n                    try {\n                        final JSONArray accounts = (JSONArray) result.get(1);\n                        if (accounts != null) {\n                            jsonObject.put(\"cookies\", accounts);\n                        }\n                    } catch (Exception e) {\n                        Log.e(TAG, \"error getting accounts\", e);\n                    }\n                    try {\n                        final JSONArray favorites = (JSONArray) result.get(2);\n                        if (favorites != null) {\n                            jsonObject.put(\"favs\", favorites);\n                        }\n                    } catch (Exception e) {\n                        Log.e(TAG, \"error getting favorites: \", e);\n                    }\n                    callback.onCreated(jsonObject.toString());\n                }\n\n                @Override\n                public void onFailure(@NonNull final Throwable t) {\n                    Log.e(TAG, \"onFailure: \", t);\n                    callback.onCreated(null);\n                }\n            }, AppExecutors.INSTANCE.getTasksThread());\n            return;\n        } catch (final Exception e) {\n            //            if (logCollector != null) logCollector.appendException(e, LogFile.UTILS_EXPORT, \"getExportString\");\n            if (BuildConfig.DEBUG) Log.e(TAG, \"\", e);\n        }\n        callback.onCreated(null);\n    }\n\n    @NonNull\n    private static ListenableFuture<JSONObject> getSettings(@NonNull final Context context) {\n        final SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);\n        return AppExecutors.INSTANCE.getTasksThread().submit(() -> {\n            final Map<String, ?> allPrefs = sharedPreferences.getAll();\n            if (allPrefs == null) {\n                return new JSONObject();\n            }\n            try {\n                final JSONObject jsonObject = new JSONObject(allPrefs);\n                jsonObject.remove(Constants.COOKIE);\n                jsonObject.remove(Constants.DEVICE_UUID);\n                jsonObject.remove(Constants.PREV_INSTALL_VERSION);\n                jsonObject.remove(Constants.BROWSER_UA_CODE);\n                jsonObject.remove(Constants.BROWSER_UA);\n                jsonObject.remove(Constants.APP_UA_CODE);\n                jsonObject.remove(Constants.APP_UA);\n                return jsonObject;\n            } catch (Exception e) {\n                Log.e(TAG, \"Error exporting settings\", e);\n            }\n            return new JSONObject();\n        });\n    }\n\n    private static ListenableFuture<JSONArray> getFavorites(final Context context) {\n        final SettableFuture<JSONArray> future = SettableFuture.create();\n        final FavoriteRepository favoriteRepository = FavoriteRepository.Companion.getInstance(context);\n        favoriteRepository.getAllFavorites(\n                CoroutineUtilsKt.getContinuation((favorites, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        future.set(new JSONArray());\n                        Log.e(TAG, \"getFavorites: \", throwable);\n                        return;\n                    }\n                    final JSONArray jsonArray = new JSONArray();\n                    try {\n                        for (final Favorite favorite : favorites) {\n                            final JSONObject jsonObject = new JSONObject();\n                            jsonObject.put(\"q\", favorite.getQuery());\n                            jsonObject.put(\"type\", favorite.getType().toString());\n                            jsonObject.put(\"s\", favorite.getDisplayName());\n                            jsonObject.put(\"pic_url\", favorite.getPicUrl());\n                            jsonObject.put(\"d\", favorite.getDateAdded().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());\n                            jsonArray.put(jsonObject);\n                        }\n                    } catch (Exception e) {\n                        if (BuildConfig.DEBUG) {\n                            Log.e(TAG, \"Error exporting favorites\", e);\n                        }\n                    }\n                    future.set(jsonArray);\n                }), Dispatchers.getIO())\n        );\n        return future;\n    }\n\n    private static ListenableFuture<JSONArray> getCookies(final Context context) {\n        final SettableFuture<JSONArray> future = SettableFuture.create();\n        final AccountRepository accountRepository = AccountRepository.Companion.getInstance(context);\n        accountRepository.getAllAccounts(\n                CoroutineUtilsKt.getContinuation((accounts, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"getCookies: \", throwable);\n                        future.set(new JSONArray());\n                        return;\n                    }\n                    final JSONArray jsonArray = new JSONArray();\n                    try {\n                        for (final Account cookie : accounts) {\n                            final JSONObject jsonObject = new JSONObject();\n                            jsonObject.put(\"i\", cookie.getUid());\n                            jsonObject.put(\"u\", cookie.getUsername());\n                            jsonObject.put(\"c\", cookie.getCookie());\n                            jsonObject.put(\"full_name\", cookie.getFullName());\n                            jsonObject.put(\"profile_pic\", cookie.getProfilePic());\n                            jsonArray.put(jsonObject);\n                        }\n                    } catch (Exception e) {\n                        if (BuildConfig.DEBUG) {\n                            Log.e(TAG, \"Error exporting accounts\", e);\n                        }\n                    }\n                    future.set(jsonArray);\n                }), Dispatchers.getIO())\n        );\n        return future;\n    }\n\n    @IntDef(value = {FLAG_COOKIES, FLAG_FAVORITES, FLAG_SETTINGS}, flag = true)\n    @interface ExportImportFlags {}\n\n    public interface OnExportStringCreatedCallback {\n        void onCreated(String exportString);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/FlavorTown.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport java.util.Objects;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport awais.instagrabber.BuildConfig;\nimport awais.instagrabber.R;\nimport awaisomereport.CrashReporterHelper;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic final class FlavorTown {\n    private static final String TAG = \"FlavorTown\";\n    private static final UpdateChecker UPDATE_CHECKER = UpdateChecker.getInstance();\n    private static final Pattern VERSION_NAME_PATTERN = Pattern.compile(\"v?(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)(?:_?)(\\\\w*)(?:-?)(\\\\w*)\");\n\n    private static boolean checking = false;\n\n    public static void updateCheck(@NonNull final AppCompatActivity context) {\n        updateCheck(context, false);\n    }\n\n    public static void updateCheck(@NonNull final AppCompatActivity context,\n                                   final boolean force) {\n        if (checking) return;\n        checking = true;\n        AppExecutors.INSTANCE.getNetworkIO().execute(() -> {\n            final String onlineVersionName = UPDATE_CHECKER.getLatestVersion();\n            if (onlineVersionName == null) return;\n            final String onlineVersion = getVersion(onlineVersionName);\n            final String localVersion = getVersion(BuildConfig.VERSION_NAME);\n            if (Objects.equals(onlineVersion, localVersion)) {\n                if (force) {\n                    AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                        final Context applicationContext = context.getApplicationContext();\n                        // Check if app was closed or crashed before reaching here\n                        if (applicationContext == null) return;\n                        // Show toast if version number preference was tapped\n                        Toast.makeText(applicationContext, R.string.on_latest_version, Toast.LENGTH_SHORT).show();\n                    });\n                }\n                return;\n            }\n            final boolean shouldShowDialog = UpdateCheckCommon.shouldShowUpdateDialog(force, onlineVersionName);\n            if (!shouldShowDialog) return;\n            UpdateCheckCommon.showUpdateDialog(context, onlineVersionName, (dialog, which) -> {\n                UPDATE_CHECKER.onDownload(context);\n                dialog.dismiss();\n            });\n        });\n    }\n\n    private static String getVersion(@NonNull final String versionName) {\n        final Matcher matcher = VERSION_NAME_PATTERN.matcher(versionName);\n        if (!matcher.matches()) return versionName;\n        try {\n            return matcher.group(1);\n        } catch (Exception e) {\n            Log.e(TAG, \"getVersion: \", e);\n        }\n        return versionName;\n    }\n\n    public static void changelogCheck(@NonNull final Context context) {\n        if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) >= BuildConfig.VERSION_CODE) return;\n        int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);\n        int browserUaCode = settingsHelper.getInteger(Constants.BROWSER_UA_CODE);\n        if (browserUaCode == -1 || browserUaCode >= UserAgentUtils.browsers.length) {\n            browserUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length);\n            settingsHelper.putInteger(Constants.BROWSER_UA_CODE, browserUaCode);\n        }\n        if (appUaCode == -1 || appUaCode >= UserAgentUtils.devices.length) {\n            appUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length);\n            settingsHelper.putInteger(Constants.APP_UA_CODE, appUaCode);\n        }\n        final String appUa = UserAgentUtils.generateAppUA(appUaCode, LocaleUtils.getCurrentLocale().getLanguage());\n        settingsHelper.putString(Constants.APP_UA, appUa);\n        final String browserUa = UserAgentUtils.generateBrowserUA(browserUaCode);\n        settingsHelper.putString(Constants.BROWSER_UA, browserUa);\n        AppExecutors.INSTANCE.getDiskIO().execute(() -> CrashReporterHelper.deleteAllStacktraceFiles(context));\n        Toast.makeText(context, R.string.updated, Toast.LENGTH_SHORT).show();\n        settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/IntentUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.net.Uri\nimport android.text.TextUtils\nimport awais.instagrabber.models.IntentModel\nimport awais.instagrabber.models.enums.IntentModelType\n\nobject IntentUtils {\n    @JvmStatic\n    fun parseUrl(url: String): IntentModel? {\n        val parsedUrl = Uri.parse(url).normalizeScheme()\n\n        // final String domain = parsedUrl.getHost().replaceFirst(\"^www\\\\.\", \"\");\n        // final boolean isHttpsUri = \"https\".equals(parsedUrl.getScheme());\n        val paths = parsedUrl.pathSegments\n        if (paths.isEmpty()) {\n            return null\n        }\n        var path = paths[0]\n        var text: String? = null\n        var type = IntentModelType.UNKNOWN\n        if (1 == paths.size) {\n            text = path\n            type = IntentModelType.USERNAME\n        } else if (\"_u\" == path) {\n            text = paths[1]\n            type = IntentModelType.USERNAME\n        } else if (\"p\" == path || \"reel\" == path || \"tv\" == path) {\n            text = paths[1]\n            type = IntentModelType.POST\n        } else if (2 < paths.size && \"explore\" == path) {\n            path = paths[1]\n            if (\"locations\" == path) {\n                text = paths[2]\n                type = IntentModelType.LOCATION\n            }\n            if (\"tags\" == path) {\n                text = paths[2]\n                type = IntentModelType.HASHTAG\n            }\n        }\n        return if (TextUtils.isEmpty(text)) {\n            null\n        } else IntentModel(type, text!!)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport awais.instagrabber.repositories.responses.Media\nimport java.util.*\nimport kotlin.collections.ArrayList\n\n//    fun filter(caption: String?): Boolean {\n//        if (caption == null) return false\n//        if (keywords.isEmpty()) return false\n//        val temp = caption.toLowerCase()\n//        for (s in keywords) {\n//            if (temp.contains(s)) return true\n//        }\n//        return false\n//    }\n\nprivate fun containsAnyKeyword(keywords: List<String>, media: Media?): Boolean {\n    if (media == null || keywords.isEmpty()) return false\n    val (_, text) = media.caption ?: return false\n    val temp = text!!.lowercase(Locale.getDefault())\n    return keywords.any { temp.contains(it) }\n}\n\nfun filter(keywords: List<String>, media: List<Media>?): List<Media>? {\n    if (keywords.isEmpty()) return media\n    if (media == null) return ArrayList()\n    val result: MutableList<Media> = ArrayList()\n    media.filterNotTo(result) { containsAnyKeyword(keywords, it) }\n    return result\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/LocaleUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.res.Configuration\nimport android.view.ContextThemeWrapper\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport java.util.*\n\n// taken from my app TESV Console Codes\nobject LocaleUtils {\n    private lateinit var defaultLocale: Locale\n\n    @JvmStatic\n    lateinit var currentLocale: Locale\n        private set\n\n    @JvmStatic\n    fun setLocale(baseContext: Context) {\n        var baseContext1 = baseContext\n        defaultLocale = Locale.getDefault()\n        if (baseContext1 is ContextThemeWrapper) baseContext1 = baseContext1.baseContext\n        if (Utils.settingsHelper == null) Utils.settingsHelper = SettingsHelper(baseContext1)\n        val appLanguageSettings = Utils.settingsHelper.getString(PreferenceKeys.APP_LANGUAGE)\n        val lang = getCorrespondingLanguageCode(appLanguageSettings)\n        currentLocale = when {\n            TextUtils.isEmpty(lang) -> defaultLocale\n            lang!!.contains(\"_\") -> {\n                val split = lang.split(\"_\")\n                Locale(split[0], split[1])\n            }\n            else -> Locale(lang)\n        }\n        currentLocale.let {\n            Locale.setDefault(it)\n            val res = baseContext1.resources\n            val config = res.configuration\n            // config.locale = currentLocale\n            config.setLocale(it)\n            config.setLayoutDirection(it)\n            res.updateConfiguration(config, res.displayMetrics)\n        }\n    }\n\n    @JvmStatic\n    fun updateConfig(wrapper: ContextThemeWrapper) {\n        if (!this::currentLocale.isInitialized) return\n        val configuration = Configuration()\n        // configuration.locale = currentLocale\n        configuration.setLocale(currentLocale)\n        wrapper.applyOverrideConfiguration(configuration)\n    }\n\n    fun getCorrespondingLanguageCode(appLanguageSettings: String): String? {\n        if (TextUtils.isEmpty(appLanguageSettings)) return null\n        when (appLanguageSettings.toInt()) {\n            1 -> return \"en\"\n            2 -> return \"fr\"\n            3 -> return \"es\"\n            4 -> return \"zh_CN\"\n            5 -> return \"in\"\n            6 -> return \"it\"\n            7 -> return \"de\"\n            8 -> return \"pl\"\n            9 -> return \"tr\"\n            10 -> return \"pt\"\n            11 -> return \"fa\"\n            12 -> return \"mk\"\n            13 -> return \"vi\"\n            14 -> return \"zh_TW\"\n            15 -> return \"ca\"\n            16 -> return \"ru\"\n            17 -> return \"hi\"\n            18 -> return \"nl\"\n            19 -> return \"sk\"\n            20 -> return \"ja\"\n            21 -> return \"el\"\n            22 -> return \"eu\"\n            23 -> return \"sv\"\n            24 -> return \"ko\"\n            25 -> return \"ar\"\n        }\n        return null\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/MediaUploadHelper.kt",
    "content": "@file:JvmName(\"MediaUploadHelper\")\n\npackage awais.instagrabber.utils\n\nimport awais.instagrabber.models.UploadPhotoOptions\nimport awais.instagrabber.models.UploadVideoOptions\nimport awais.instagrabber.models.enums.MediaItemType\nimport org.json.JSONObject\nimport java.time.Instant\nimport java.util.*\nimport kotlin.random.Random\n\n\nprivate const val LOWER = 1000000000L\nprivate const val UPPER = 9999999999L\n\nprivate fun createPhotoRuploadParams(options: UploadPhotoOptions): Map<String, String> {\n    val imageCompression = mapOf(\n        \"lib_name\" to \"moz\",\n        \"lib_version\" to \"3.1.m\",\n        \"quality\" to \"80\",\n    )\n    return listOfNotNull(\n        \"retry_context\" to retryContextString,\n        \"media_type\" to \"1\",\n        \"upload_id\" to (options.uploadId ?: \"\"),\n        \"xsharing_user_ids\" to \"[]\",\n        \"image_compression\" to JSONObject(imageCompression).toString(),\n        if (options.isSideCar) \"is_sidecar\" to \"1\" else null,\n    ).toMap()\n}\n\nprivate fun createVideoRuploadParams(options: UploadVideoOptions): Map<String, String> = listOfNotNull(\n    \"retry_context\" to retryContextString,\n    \"media_type\" to \"2\",\n    \"xsharing_user_ids\" to \"[]\",\n    \"upload_id\" to options.uploadId,\n    \"upload_media_width\" to options.width.toString(),\n    \"upload_media_height\" to options.height.toString(),\n    \"upload_media_duration_ms\" to options.duration.toString(),\n    if (options.isSideCar) \"is_sidecar\" to \"1\" else null,\n    if (options.forAlbum) \"for_album\" to \"1\" else null,\n    if (options.isDirect) \"direct_v2\" to \"1\" else null,\n    *(if (options.isForDirectStory) arrayOf(\n        \"for_direct_story\" to \"1\",\n        \"content_tags\" to \"\"\n    ) else emptyArray()),\n    if (options.isIgtvVideo) \"is_igtv_video\" to \"1\" else null,\n    if (options.isDirectVoice) \"is_direct_voice\" to \"1\" else null,\n).toMap()\n\nval retryContextString: String\n    get() {\n        return JSONObject(\n            mapOf(\n                \"num_step_auto_retry\" to 0,\n                \"num_reupload\" to 0,\n                \"num_step_manual_retry\" to 0,\n            )\n        ).toString()\n    }\n\nfun createUploadPhotoOptions(byteLength: Long): UploadPhotoOptions {\n    val uploadId = generateUploadId()\n    return UploadPhotoOptions(\n        uploadId,\n        generateName(uploadId),\n        byteLength,\n    )\n}\n\nfun createUploadDmVideoOptions(\n    byteLength: Long,\n    duration: Long,\n    width: Int,\n    height: Int\n): UploadVideoOptions {\n    val uploadId = generateUploadId()\n    return UploadVideoOptions(\n        uploadId,\n        generateName(uploadId),\n        byteLength,\n        duration,\n        width,\n        height,\n        isDirect = true,\n        mediaType = MediaItemType.MEDIA_TYPE_VIDEO,\n    )\n}\n\nfun createUploadDmVoiceOptions(\n    byteLength: Long,\n    duration: Long\n): UploadVideoOptions {\n    val uploadId = generateUploadId()\n    return UploadVideoOptions(\n        uploadId,\n        generateName(uploadId),\n        byteLength,\n        duration,\n        isDirectVoice = true,\n        mediaType = MediaItemType.MEDIA_TYPE_VOICE,\n    )\n}\n\nfun generateUploadId(): String {\n    return Instant.now().epochSecond.toString()\n}\n\nfun generateName(uploadId: String): String {\n    val random = Random.nextLong(LOWER, UPPER + 1)\n    return \"${uploadId}_0_$random\"\n}\n\nfun getUploadPhotoHeaders(options: UploadPhotoOptions): Map<String, String> {\n    val waterfallId = options.waterfallId ?: UUID.randomUUID().toString()\n    val contentLength = options.byteLength.toString()\n    return mapOf(\n        \"X_FB_PHOTO_WATERFALL_ID\" to waterfallId,\n        \"X-Entity-Type\" to \"image/jpeg\",\n        \"Offset\" to \"0\",\n        \"X-Instagram-Rupload-Params\" to JSONObject(createPhotoRuploadParams(options)).toString(),\n        \"X-Entity-Name\" to options.name,\n        \"X-Entity-Length\" to contentLength,\n        \"Content-Type\" to \"application/octet-stream\",\n        \"Content-Length\" to contentLength,\n        \"Accept-Encoding\" to \"gzip\",\n    )\n}\n\nfun getUploadVideoHeaders(options: UploadVideoOptions): Map<String, String> {\n    val ruploadParams = createVideoRuploadParams(options)\n    val waterfallId = options.waterfallId ?: UUID.randomUUID().toString()\n    val contentLength = options.byteLength.toString()\n    return getBaseUploadVideoHeaders(ruploadParams) + mapOf(\n        \"X_FB_PHOTO_WATERFALL_ID\" to waterfallId,\n        \"X-Entity-Type\" to \"video/mp4\",\n        \"Offset\" to (if (options.offset > 0) options.offset else 0).toString(),\n        \"X-Entity-Name\" to options.name,\n        \"X-Entity-Length\" to contentLength,\n        \"Content-Type\" to \"application/octet-stream\",\n        \"Content-Length\" to contentLength,\n    )\n}\n\nprivate fun getBaseUploadVideoHeaders(ruploadParams: Map<String, String>): Map<String, String> {\n    return mapOf(\n        \"X-IG-Connection-Type\" to \"WIFI\",\n        \"X-IG-Capabilities\" to \"3brTvwE=\",\n        \"Accept-Encoding\" to \"gzip\",\n        \"X-Instagram-Rupload-Params\" to JSONObject(ruploadParams).toString()\n    )\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/MediaUploader.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.ContentResolver\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport androidx.documentfile.provider.DocumentFile\nimport awais.instagrabber.models.UploadVideoOptions\nimport awais.instagrabber.webservices.interceptors.AddCookiesInterceptor\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport okhttp3.*\nimport okio.BufferedSink\nimport okio.Okio\nimport org.json.JSONObject\nimport ru.gildor.coroutines.okhttp.await\nimport java.io.IOException\nimport java.io.InputStream\n\nobject MediaUploader {\n    private const val HOST = \"https://i.instagram.com\"\n    private val octetStreamMediaType: MediaType = requireNotNull(MediaType.parse(\"application/octet-stream\")) {\n        \"No media type found for application/octet-stream\"\n    }\n\n    suspend fun uploadPhoto(\n        uri: Uri,\n        contentResolver: ContentResolver,\n    ): MediaUploadResponse = withContext(Dispatchers.IO) {\n        val bitmapResult = BitmapUtils.loadBitmap(contentResolver, uri, 1000f, false)\n        val bitmap = bitmapResult?.bitmap ?: throw IOException(\"bitmap is null\")\n        uploadPhoto(contentResolver, bitmap)\n    }\n\n    @Suppress(\"BlockingMethodInNonBlockingContext\")\n    private suspend fun uploadPhoto(\n        contentResolver: ContentResolver,\n        bitmap: Bitmap,\n    ): MediaUploadResponse = withContext(Dispatchers.IO) {\n        val file: DocumentFile? = BitmapUtils.convertToJpegAndSaveToFile(contentResolver, bitmap, null)\n        val byteLength: Long = file!!.length()\n        val options = createUploadPhotoOptions(byteLength)\n        val headers = getUploadPhotoHeaders(options)\n        val url = HOST + \"/rupload_igphoto/\" + options.name + \"/\"\n        try {\n            contentResolver.openInputStream(file.uri).use { input ->\n                upload(input!!, url, headers)\n            }\n        } finally {\n            file.delete()\n        }\n    }\n\n    @JvmStatic\n    @Suppress(\"BlockingMethodInNonBlockingContext\") // See https://youtrack.jetbrains.com/issue/KTIJ-838\n    suspend fun uploadVideo(\n        uri: Uri,\n        contentResolver: ContentResolver,\n        options: UploadVideoOptions,\n    ): MediaUploadResponse = withContext(Dispatchers.IO) {\n        val headers = getUploadVideoHeaders(options)\n        val url = HOST + \"/rupload_igvideo/\" + options.name + \"/\"\n        contentResolver.openInputStream(uri).use { input ->\n            if (input == null) {\n                // listener.onFailure(RuntimeException(\"InputStream was null\"))\n                throw IllegalStateException(\"InputStream was null\")\n            }\n            upload(input, url, headers)\n        }\n    }\n\n    @Throws(IOException::class)\n    private suspend fun upload(\n        input: InputStream,\n        url: String,\n        headers: Map<String, String>,\n    ): MediaUploadResponse {\n        try {\n            val client = OkHttpClient.Builder()\n                // .addInterceptor(new LoggingInterceptor())\n                .addInterceptor(AddCookiesInterceptor())\n                .followRedirects(false)\n                .followSslRedirects(false)\n                .build()\n            val request = Request.Builder()\n                .headers(Headers.of(headers))\n                .url(url)\n                .post(create(octetStreamMediaType, input))\n                .build()\n            return withContext(Dispatchers.IO) {\n                val response = client.newCall(request).await()\n                val body = response.body()\n                @Suppress(\"BlockingMethodInNonBlockingContext\") // Blocked by https://github.com/square/okio/issues/501\n                MediaUploadResponse(response.code(), if (body != null) JSONObject(body.string()) else null)\n            }\n        } catch (e: Exception) {\n            // rethrow for proper stacktrace. See https://github.com/gildor/kotlin-coroutines-okhttp/tree/master#wrap-exception-manually\n            throw IOException(e)\n        }\n    }\n\n    private fun create(mediaType: MediaType, inputStream: InputStream): RequestBody = object : RequestBody() {\n        override fun contentType(): MediaType {\n            return mediaType\n        }\n\n        override fun contentLength(): Long {\n            return try {\n                inputStream.available().toLong()\n            } catch (e: IOException) {\n                0\n            }\n        }\n\n        @Throws(IOException::class)\n        @Suppress(\"DEPRECATION_ERROR\")\n        override fun writeTo(sink: BufferedSink) {\n            Okio.source(inputStream).use { sink.writeAll(it) }\n        }\n    }\n\n    data class MediaUploadResponse(val responseCode: Int, val response: JSONObject?)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/MediaUtils.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.content.ContentResolver;\nimport android.database.Cursor;\nimport android.media.MediaMetadataRetriever;\nimport android.net.Uri;\nimport android.os.ParcelFileDescriptor;\nimport android.provider.MediaStore;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.io.FileDescriptor;\n\npublic final class MediaUtils {\n    private static final String TAG = MediaUtils.class.getSimpleName();\n\n    public static void getVideoInfo(@NonNull final ContentResolver contentResolver,\n                                    @NonNull final Uri uri,\n                                    @NonNull final OnInfoLoadListener<VideoInfo> listener) {\n        getInfo(contentResolver, uri, listener, true);\n    }\n\n    public static void getVoiceInfo(@NonNull final ContentResolver contentResolver,\n                                    @NonNull final Uri uri,\n                                    @NonNull final OnInfoLoadListener<VideoInfo> listener) {\n        getInfo(contentResolver, uri, listener, false);\n    }\n\n    private static void getInfo(@NonNull final ContentResolver contentResolver,\n                                @NonNull final Uri uri,\n                                @NonNull final OnInfoLoadListener<VideoInfo> listener,\n                                @NonNull final Boolean isVideo) {\n        AppExecutors.INSTANCE.getTasksThread().submit(() -> {\n            try (ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, \"r\")) {\n                if (parcelFileDescriptor == null) {\n                    listener.onLoad(null);\n                    return;\n                }\n                final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();\n                final MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();\n                mediaMetadataRetriever.setDataSource(fileDescriptor);\n                String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);\n                if (TextUtils.isEmpty(duration)) duration = \"0\";\n                if (isVideo) {\n                    String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);\n                    if (TextUtils.isEmpty(width)) width = \"1\";\n                    String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);\n                    if (TextUtils.isEmpty(height)) height = \"1\";\n                    final Cursor cursor = contentResolver.query(uri, new String[]{MediaStore.Video.Media.SIZE}, null, null, null);\n                    cursor.moveToFirst();\n                    final long fileSize = cursor.getLong(0);\n                    cursor.close();\n                    listener.onLoad(new VideoInfo(\n                            Long.parseLong(duration),\n                            Integer.valueOf(width),\n                            Integer.valueOf(height),\n                            fileSize\n                    ));\n                    return;\n                }\n                listener.onLoad(new VideoInfo(\n                        Long.parseLong(duration),\n                        0,\n                        0,\n                        0\n                ));\n            } catch (Exception e) {\n                Log.e(TAG, \"getInfo: \", e);\n                listener.onFailure(e);\n            }\n        });\n    }\n\n    public static class VideoInfo {\n        public long duration;\n        public int width;\n        public int height;\n        public long size;\n\n        public VideoInfo(final long duration, final int width, final int height, final long size) {\n            this.duration = duration;\n            this.width = width;\n            this.height = height;\n            this.size = size;\n        }\n\n    }\n\n    public interface OnInfoLoadListener<T> {\n        void onLoad(@Nullable T info);\n\n        void onFailure(Throwable t);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java",
    "content": "// package awais.instagrabber.utils;\n//\n// import android.annotation.SuppressLint;\n// import android.content.Intent;\n// import android.util.Log;\n// import android.util.SparseArray;\n//\n// import androidx.annotation.NonNull;\n// import androidx.fragment.app.Fragment;\n// import androidx.fragment.app.FragmentManager;\n// import androidx.fragment.app.FragmentTransaction;\n// import androidx.lifecycle.LiveData;\n// import androidx.lifecycle.MutableLiveData;\n// import androidx.navigation.NavController;\n// import androidx.navigation.NavDestination;\n// import androidx.navigation.NavGraph;\n// import androidx.navigation.fragment.NavHostFragment;\n//\n// import com.google.android.material.bottomnavigation.BottomNavigationView;\n//\n// import java.util.List;\n//\n// import awais.instagrabber.R;\n// import awais.instagrabber.customviews.NavHostFragmentWithDefaultAnimations;\n// import awais.instagrabber.fragments.main.FeedFragment;\n//\n// /**\n//  * This is a Java rewrite of <a href=\"https://github.com/android/architecture-components-samples/blob/master/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt\">NavigationExtensions</a>\n//  * from architecture-components-samples. Some modifications have been done, check git history.\n//  */\n// public class NavigationExtensions {\n//     private static final String TAG = NavigationExtensions.class.getSimpleName();\n//     private static String selectedItemTag;\n//     private static boolean isOnFirstFragment;\n//\n//     @NonNull\n//     public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView,\n//                                                                  @NonNull List<Integer> navGraphIds,\n//                                                                  @NonNull final FragmentManager fragmentManager,\n//                                                                  final int containerId,\n//                                                                  @NonNull Intent intent,\n//                                                                  final int firstFragmentGraphIndex) {\n//         final SparseArray<String> graphIdToTagMap = new SparseArray<>();\n//         final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>();\n//         int firstFragmentGraphId = 0;\n//         for (int i = 0; i < navGraphIds.size(); i++) {\n//             final int navGraphId = navGraphIds.get(i);\n//             final String fragmentTag = getFragmentTag(navGraphId);\n//             final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);\n//             final NavController navController = navHostFragment.getNavController();\n//             final int graphId = navController.getGraph().getId();\n//             if (i == firstFragmentGraphIndex) {\n//                 firstFragmentGraphId = graphId;\n//             }\n//             graphIdToTagMap.put(graphId, fragmentTag);\n//             if (bottomNavigationView.getSelectedItemId() == graphId) {\n//                 selectedNavController.setValue(navHostFragment.getNavController());\n//                 attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex);\n//             } else {\n//                 detachNavHostFragment(fragmentManager, navHostFragment);\n//             }\n//         }\n//         selectedItemTag = graphIdToTagMap.get(bottomNavigationView.getSelectedItemId());\n//         final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId);\n//         isOnFirstFragment = selectedItemTag != null && selectedItemTag.equals(firstFragmentTag);\n//         bottomNavigationView.setOnItemSelectedListener(item -> {\n//             if (fragmentManager.isStateSaved()) {\n//                 return false;\n//             }\n//             String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());\n//             String tag = selectedItemTag;\n//             if (tag != null && !tag.equals(newlySelectedItemTag)) {\n//                 fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);\n//                 Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag);\n//                 if (fragment == null) {\n//                     return false;\n//                     // throw new RuntimeException(\"null cannot be cast to non-null NavHostFragment\");\n//                 }\n//                 final NavHostFragment selectedFragment = (NavHostFragment) fragment;\n//                 if (firstFragmentTag != null && !firstFragmentTag.equals(newlySelectedItemTag)) {\n//                     FragmentTransaction fragmentTransaction = fragmentManager\n//                             .beginTransaction()\n//                             .setCustomAnimations(\n//                                     R.anim.nav_default_enter_anim,\n//                                     R.anim.nav_default_exit_anim,\n//                                     R.anim.nav_default_pop_enter_anim,\n//                                     R.anim.nav_default_pop_exit_anim\n//                             )\n//                             .attach(selectedFragment)\n//                             .setPrimaryNavigationFragment(selectedFragment);\n//                     for (int i = 0; i < graphIdToTagMap.size(); i++) {\n//                         final int key = graphIdToTagMap.keyAt(i);\n//                         final String fragmentTagForId = graphIdToTagMap.get(key);\n//                         if (!fragmentTagForId.equals(newlySelectedItemTag)) {\n//                             final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag);\n//                             if (fragmentByTag == null) {\n//                                 continue;\n//                             }\n//                             fragmentTransaction.detach(fragmentByTag);\n//                         }\n//                     }\n//                     fragmentTransaction.addToBackStack(firstFragmentTag)\n//                                        .setReorderingAllowed(true)\n//                                        .commit();\n//                 }\n//                 selectedItemTag = newlySelectedItemTag;\n//                 isOnFirstFragment = selectedItemTag.equals(firstFragmentTag);\n//                 selectedNavController.setValue(selectedFragment.getNavController());\n//                 return true;\n//             }\n//             return false;\n//         });\n//         setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager);\n//         setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent);\n//         final int finalFirstFragmentGraphId = firstFragmentGraphId;\n//         fragmentManager.addOnBackStackChangedListener(() -> {\n//             if (!isOnFirstFragment) {\n//                 if (firstFragmentTag == null) {\n//                     return;\n//                 }\n//                 if (!isOnBackStack(fragmentManager, firstFragmentTag)) {\n//                     bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId);\n//                 }\n//             }\n//\n//             final NavController navController = selectedNavController.getValue();\n//             if (navController != null && navController.getCurrentDestination() == null) {\n//                 final NavGraph navControllerGraph = navController.getGraph();\n//                 navController.navigate(navControllerGraph.getId());\n//             }\n//         });\n//         return selectedNavController;\n//     }\n//\n//     private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager,\n//                                                          final String fragmentTag,\n//                                                          final int navGraphId,\n//                                                          final int containerId) {\n//         final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag);\n//         if (existingFragment != null) {\n//             return existingFragment;\n//         }\n//         final NavHostFragment navHostFragment = NavHostFragmentWithDefaultAnimations.create(navGraphId);\n//         fragmentManager.beginTransaction()\n//                        .setReorderingAllowed(true)\n//                        .add(containerId, navHostFragment, fragmentTag)\n//                        .commitNow();\n//         return navHostFragment;\n//     }\n//\n//     private static void attachNavHostFragment(final FragmentManager fragmentManager,\n//                                               final NavHostFragment navHostFragment,\n//                                               final boolean isPrimaryNavFragment) {\n//         final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()\n//                                                                        .attach(navHostFragment);\n//         if (isPrimaryNavFragment) {\n//             fragmentTransaction.setPrimaryNavigationFragment(navHostFragment);\n//         }\n//         fragmentTransaction.commitNow();\n//     }\n//\n//     private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) {\n//         fragmentManager.beginTransaction()\n//                        .detach(navHostFragment)\n//                        .commitNow();\n//     }\n//\n//     @SuppressLint(\"RestrictedApi\")\n//     private static void setupItemReselected(final BottomNavigationView bottomNavigationView,\n//                                             final SparseArray<String> graphIdToTagMap,\n//                                             final FragmentManager fragmentManager) {\n//         bottomNavigationView.setOnItemReselectedListener(item -> {\n//             final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());\n//             final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag);\n//             if (fragmentByTag == null) {\n//                 return;\n//             }\n//             final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag;\n//             final NavController navController = selectedFragment.getNavController();\n//             final NavGraph navControllerGraph = navController.getGraph();\n//             final NavDestination currentDestination = navController.getCurrentDestination();\n//             final int startDestination = navControllerGraph.getStartDestination();\n//             int backStackSize = navController.getBackStack().size();\n//             if (currentDestination != null && backStackSize == 2 && currentDestination.getId() == startDestination) {\n//                 // scroll to top\n//                 final List<Fragment> fragments = selectedFragment.getChildFragmentManager().getFragments();\n//                 if (fragments.isEmpty()) return;\n//                 final Fragment fragment = fragments.get(0);\n//                 if (fragment instanceof FeedFragment) {\n//                     ((FeedFragment) fragment).scrollToTop();\n//                 }\n//                 return;\n//             }\n//             final boolean popped = navController.popBackStack(startDestination, false);\n//             backStackSize = navController.getBackStack().size();\n//             if (!popped || backStackSize > 2) {\n//                 try {\n//                     // try loop pop\n//                     do {\n//                         navController.popBackStack();\n//                         backStackSize = navController.getBackStack().size();\n//                     } while (backStackSize > 2);\n//                 } catch (Exception e) {\n//                     Log.e(TAG, \"setupItemReselected: \", e);\n//                 }\n//             }\n//         });\n//     }\n//\n//     private static void setupDeepLinks(final BottomNavigationView bottomNavigationView,\n//                                        final List<Integer> navGraphIds,\n//                                        final FragmentManager fragmentManager,\n//                                        final int containerId,\n//                                        final Intent intent) {\n//         for (int i = 0; i < navGraphIds.size(); i++) {\n//             final int navGraphId = navGraphIds.get(i);\n//             final String fragmentTag = getFragmentTag(navGraphId);\n//             final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);\n//             if (navHostFragment.getNavController().handleDeepLink(intent)) {\n//                 final int selectedItemId = bottomNavigationView.getSelectedItemId();\n//                 NavController navController = navHostFragment.getNavController();\n//                 NavGraph graph = navController.getGraph();\n//                 if (selectedItemId != graph.getId()) {\n//                     navController = navHostFragment.getNavController();\n//                     graph = navController.getGraph();\n//                     bottomNavigationView.setSelectedItemId(graph.getId());\n//                 }\n//             }\n//         }\n//     }\n//\n//     private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) {\n//         int backStackCount = fragmentManager.getBackStackEntryCount();\n//         for (int i = 0; i < backStackCount; i++) {\n//             final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);\n//             final String name = backStackEntry.getName();\n//             if (name != null && name.equals(backStackName)) {\n//                 return true;\n//             }\n//         }\n//         return false;\n//     }\n//\n//     private static String getFragmentTag(final int index) {\n//         return \"bottomNavigation#\" + index;\n//     }\n// }\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/NavigationHelper.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.res.Resources\nimport androidx.annotation.ArrayRes\nimport awais.instagrabber.R\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.models.Tab\n\nvar tabOrderString: String? = null\n\nprivate val NON_REMOVABLE_NAV_ROOT_IDS: List<Int> = listOf(R.id.profile_nav_graph, R.id.more_nav_graph)\n\n\nfun getLoggedInNavTabs(context: Context): Pair<List<Tab>, List<Tab>> {\n    val navRootIds = getArrayResIds(context.resources, R.array.logged_in_nav_root_ids)\n    return getTabs(context, navRootIds)\n}\n\nfun getAnonNavTabs(context: Context): List<Tab> {\n    val navRootIds = getArrayResIds(context.resources, R.array.anon_nav_root_ids)\n    val (tabs, _) = getTabs(context, navRootIds, true)\n    return tabs\n}\n\nprivate fun getTabs(\n    context: Context,\n    navRootIds: IntArray,\n    isAnon: Boolean = false,\n): Pair<List<Tab>, MutableList<Tab>> {\n    val navGraphNames = getResIdsForNavRootIds(navRootIds, ::getNavGraphNameForNavRootId)\n    val navGraphResIds = getResIdsForNavRootIds(navRootIds, ::getNavGraphResIdForNavRootId)\n    val titleArray = getResIdsForNavRootIds(navRootIds, ::getTitleResIdForNavRootId)\n    val iconIds = getResIdsForNavRootIds(navRootIds, ::getIconResIdForNavRootId)\n    val startDestFragIds = getResIdsForNavRootIds(navRootIds, ::getStartDestFragIdForNavRootId)\n    val (orderedGraphNames, orderedNavRootIds) = if (isAnon) navGraphNames to navRootIds.toList() else getOrderedNavRootIdsFromPref(navGraphNames)\n    val tabs = mutableListOf<Tab>()\n    val otherTabs = mutableListOf<Tab>() // Will contain tabs not in current list\n    for (i in navRootIds.indices) {\n        val navRootId = navRootIds[i]\n        val tab = Tab(\n            iconIds[i],\n            context.getString(titleArray[i]),\n            if (isAnon) false else !NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),\n            navGraphResIds[i],\n            navRootId,\n            startDestFragIds[i]\n        )\n        if (!isAnon && !orderedGraphNames.contains(navGraphNames[i])) {\n            otherTabs.add(tab)\n            continue\n        }\n        tabs.add(tab)\n    }\n    val associateBy = tabs.associateBy { it.navigationRootId }\n    val orderedTabs = orderedNavRootIds.mapNotNull { associateBy[it] }\n    return orderedTabs to otherTabs\n}\n\nprivate fun getArrayResIds(resources: Resources, @ArrayRes arrayRes: Int): IntArray {\n    val typedArray = resources.obtainTypedArray(arrayRes)\n    val length = typedArray.length()\n    val navRootIds = IntArray(length)\n    for (i in 0 until length) {\n        val resourceId = typedArray.getResourceId(i, 0)\n        if (resourceId == 0) continue\n        navRootIds[i] = resourceId\n    }\n    typedArray.recycle()\n    return navRootIds\n}\n\nprivate fun <T> getResIdsForNavRootIds(navRootIds: IntArray, resMapper: Function1<Int, T>): List<T> = navRootIds\n    .asSequence()\n    .filterNot { it == 0 }\n    .map(resMapper)\n    .filterNot { it == 0 }\n    .toList()\n\nprivate fun getTitleResIdForNavRootId(id: Int): Int = when (id) {\n    R.id.direct_messages_nav_graph -> R.string.title_dm\n    R.id.feed_nav_graph -> R.string.feed\n    R.id.profile_nav_graph -> R.string.profile\n    R.id.discover_nav_graph -> R.string.title_discover\n    R.id.more_nav_graph -> R.string.more\n    R.id.favorites_nav_graph -> R.string.title_favorites\n    R.id.notification_viewer_nav_graph -> R.string.title_notifications\n    else -> 0\n}\n\nprivate fun getIconResIdForNavRootId(id: Int): Int = when (id) {\n    R.id.direct_messages_nav_graph -> R.drawable.ic_message_24\n    R.id.feed_nav_graph -> R.drawable.ic_home_24\n    R.id.profile_nav_graph -> R.drawable.ic_person_24\n    R.id.discover_nav_graph -> R.drawable.ic_explore_24\n    R.id.more_nav_graph -> R.drawable.ic_more_horiz_24\n    R.id.favorites_nav_graph -> R.drawable.ic_star_24\n    R.id.notification_viewer_nav_graph -> R.drawable.ic_not_liked\n    else -> 0\n}\n\nprivate fun getStartDestFragIdForNavRootId(id: Int): Int = when (id) {\n    R.id.direct_messages_nav_graph -> R.id.directMessagesInboxFragment\n    R.id.feed_nav_graph -> R.id.feedFragment\n    R.id.profile_nav_graph -> R.id.profileFragment\n    R.id.discover_nav_graph -> R.id.discoverFragment\n    R.id.more_nav_graph -> R.id.morePreferencesFragment\n    R.id.favorites_nav_graph -> R.id.favoritesFragment\n    R.id.notification_viewer_nav_graph -> R.id.notificationsViewer\n    else -> 0\n}\n\nfun getNavGraphNameForNavRootId(id: Int): String = when (id) {\n    R.id.direct_messages_nav_graph -> \"direct_messages_nav_graph\"\n    R.id.feed_nav_graph -> \"feed_nav_graph\"\n    R.id.profile_nav_graph -> \"profile_nav_graph\"\n    R.id.discover_nav_graph -> \"discover_nav_graph\"\n    R.id.more_nav_graph -> \"more_nav_graph\"\n    R.id.favorites_nav_graph -> \"favorites_nav_graph\"\n    R.id.notification_viewer_nav_graph -> \"notification_viewer_nav_graph\"\n    else -> \"\"\n}\n\nfun getNavGraphResIdForNavRootId(id: Int): Int = when (id) {\n    R.id.direct_messages_nav_graph -> R.navigation.direct_messages_nav_graph\n    R.id.feed_nav_graph -> R.navigation.feed_nav_graph\n    R.id.profile_nav_graph -> R.navigation.profile_nav_graph\n    R.id.discover_nav_graph -> R.navigation.discover_nav_graph\n    R.id.more_nav_graph -> R.navigation.more_nav_graph\n    R.id.favorites_nav_graph -> R.navigation.favorites_nav_graph\n    R.id.notification_viewer_nav_graph -> R.navigation.notification_viewer_nav_graph\n    else -> 0\n}\n\nprivate fun getNavRootIdForGraphName(navGraphName: String): Int = when (navGraphName) {\n    \"direct_messages_nav_graph\" -> R.id.direct_messages_nav_graph\n    \"feed_nav_graph\" -> R.id.feed_nav_graph\n    \"profile_nav_graph\" -> R.id.profile_nav_graph\n    \"discover_nav_graph\" -> R.id.discover_nav_graph\n    \"more_nav_graph\" -> R.id.more_nav_graph\n    \"favorites_nav_graph\" -> R.id.favorites_nav_graph\n    \"notification_viewer_nav_graph\" -> R.id.notification_viewer_nav_graph\n    else -> 0\n}\n\nprivate fun getOrderedNavRootIdsFromPref(navGraphNames: List<String>): Pair<List<String>, List<Int>> {\n    tabOrderString = Utils.settingsHelper.getString(PreferenceKeys.PREF_TAB_ORDER)\n    if (tabOrderString.isNullOrBlank()) {\n        // Use top 5 entries for default list\n        val top5navGraphNames: List<String> = navGraphNames.subList(0, 5)\n        val newOrderString = top5navGraphNames.joinToString(\",\")\n        Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString)\n        tabOrderString = newOrderString\n        return top5navGraphNames to top5navGraphNames.map(::getNavRootIdForGraphName)\n    }\n    val orderString = tabOrderString ?: return navGraphNames to navGraphNames.subList(0, 5).map(::getNavRootIdForGraphName)\n    // Make sure that the list from preference does not contain any invalid values\n    val orderGraphNames = orderString\n        .split(\",\")\n        .asSequence()\n        .filter(String::isNotBlank)\n        .filter(navGraphNames::contains)\n        .toList()\n    val graphNames = if (orderGraphNames.isEmpty()) {\n        // Use top 5 entries for default list\n        navGraphNames.subList(0, 5)\n    } else orderGraphNames\n    return graphNames to graphNames.map(::getNavRootIdForGraphName)\n}\n\nfun isNavRootInCurrentTabs(navRootString: String?): Boolean {\n    val navRoot = navRootString ?: return false\n    return tabOrderString?.contains(navRoot) ?: false\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/NetworkUtils.java",
    "content": "package awais.instagrabber.utils;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.net.HttpURLConnection;\nimport java.util.Map;\nimport java.util.Set;\n\npublic final class NetworkUtils {\n    @NonNull\n    public static String readFromConnection(@NonNull final HttpURLConnection conn) throws Exception {\n        final InputStream inputStream = conn.getInputStream();\n        return readFromInputStream(inputStream);\n    }\n\n    @NonNull\n    public static String readFromInputStream(final InputStream inputStream) throws IOException {\n        final StringBuilder sb = new StringBuilder();\n        try (final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {\n            String line;\n            while ((line = br.readLine()) != null) sb.append(line).append('\\n');\n        }\n        return sb.toString();\n    }\n\n    public static void setConnectionHeaders(final HttpURLConnection connection, final Map<String, String> headers) {\n        if (connection == null || headers == null || headers.isEmpty()) {\n            return;\n        }\n        for (Map.Entry<String, String> header : headers.entrySet()) {\n            connection.setRequestProperty(header.getKey(), header.getValue());\n        }\n    }\n\n    public static String getQueryString(final Map<String, String> queryParamsMap) {\n        if (queryParamsMap == null || queryParamsMap.isEmpty()) {\n            return \"\";\n        }\n        final Set<Map.Entry<String, String>> params = queryParamsMap.entrySet();\n        final StringBuilder builder = new StringBuilder();\n        for (final Map.Entry<String, String> param : params) {\n            if (TextUtils.isEmpty(param.getKey())) {\n                continue;\n            }\n            if (builder.length() != 0) {\n                builder.append(\"&\");\n            }\n            builder.append(param.getKey());\n            builder.append(\"=\");\n            builder.append(param.getValue() != null ? param.getValue() : \"\");\n        }\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/NullSafePair.kt",
    "content": "package awais.instagrabber.utils\n\n/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Container to ease passing around a tuple of two objects. This object provides a sensible\n * implementation of equals(), returning true if equals() is true on each of the contained\n * objects.\n */\n/**\n * Constructor for a Pair.\n *\n * @param first  the first object in the Pair\n * @param second the second object in the pair\n */\ndata class NullSafePair<F, S>(@JvmField val first: F, @JvmField val second: S) {\n    companion object {\n        /**\n         * Convenience method for creating an appropriately typed pair.\n         *\n         * @param a the first object in the Pair\n         * @param b the second object in the pair\n         * @return a Pair that is templatized with the types of a and b\n         */\n        fun <A, B> create(a: A, b: B): NullSafePair<A, B> {\n            return NullSafePair(a, b)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/NumberUtils.kt",
    "content": "@file:JvmName(\"NumberUtils\")\n\npackage awais.instagrabber.utils\n\nimport java.util.*\nimport kotlin.math.ln\nimport kotlin.math.pow\n\nfun getResultingHeight(requiredWidth: Int, height: Int, width: Int): Int {\n    return requiredWidth * height / width\n}\n\nfun getResultingWidth(requiredHeight: Int, height: Int, width: Int): Int {\n    return requiredHeight * width / height\n}\n\n// TODO Replace all usages with kotlin Random.nextLong() once converted to kotlin\nfun random(origin: Long, bound: Long): Long {\n    val random = Random()\n    var r = random.nextLong()\n    val n = bound - origin\n    val m = n - 1\n    when {\n        n and m == 0L -> r = (r and m) + origin // power of two\n        n > 0L -> {\n            // reject over-represented candidates\n            var u = r ushr 1 // ensure non-negative\n            while (u + m - u % n.also { r = it } < 0L) { // rejection check\n                // retry\n                u = random.nextLong() ushr 1\n            }\n            r += origin\n        }\n        else -> {\n            // range not representable as long\n            while (r < origin || r >= bound) r = random.nextLong()\n        }\n    }\n    return r\n}\n\nfun calculateWidthHeight(height: Int, width: Int, maxHeight: Int, maxWidth: Int): NullSafePair<Int, Int> {\n    if (width > maxWidth) {\n        var tempHeight = getResultingHeight(maxWidth, height, width)\n        var tempWidth = maxWidth\n        if (tempHeight > maxHeight) {\n            tempWidth = getResultingWidth(maxHeight, tempHeight, tempWidth)\n            tempHeight = maxHeight\n        }\n        return NullSafePair(tempWidth, tempHeight)\n    }\n    if (height < maxHeight && width < maxWidth || height > maxHeight) {\n        var tempWidth = getResultingWidth(maxHeight, height, width)\n        var tempHeight = maxHeight\n        if (tempWidth > maxWidth) {\n            tempHeight = getResultingHeight(maxWidth, tempHeight, tempWidth)\n            tempWidth = maxWidth\n        }\n        return NullSafePair(tempWidth, tempHeight)\n    }\n    return NullSafePair(width, height)\n}\n\nfun roundFloat2Decimals(value: Float): Float {\n    return ((value + (if (value >= 0) 1 else -1) * 0.005f) * 100).toInt() / 100f\n}\n\nfun abbreviate(number: Long, options: AbbreviateOptions? = null): String {\n    // adapted from https://stackoverflow.com/a/9769590/1436766\n    var threshold = 1000\n    var addSpace = false\n    if (options != null) {\n        threshold = options.threshold\n        addSpace = options.addSpaceBeforePrefix\n    }\n    if (number < threshold) return \"\" + number\n    val exp = (ln(number.toDouble()) / ln(threshold.toDouble())).toInt()\n    return String.format(\n        Locale.US,\n        \"%.1f%s%c\",\n        number / threshold.toDouble().pow(exp.toDouble()),\n        if (addSpace) \" \" else \"\",\n        \"kMGTPE\"[exp - 1]\n    )\n}\n\ndata class AbbreviateOptions(val threshold: Int = 1000, val addSpaceBeforePrefix: Boolean = false)"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/PasswordUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.util.Base64\nimport java.security.GeneralSecurityException\nimport java.security.InvalidAlgorithmParameterException\nimport java.security.InvalidKeyException\nimport java.security.NoSuchAlgorithmException\nimport javax.crypto.BadPaddingException\nimport javax.crypto.Cipher\nimport javax.crypto.IllegalBlockSizeException\nimport javax.crypto.NoSuchPaddingException\nimport javax.crypto.spec.IvParameterSpec\nimport javax.crypto.spec.SecretKeySpec\n\nobject PasswordUtils {\n    private const val cipherAlgo = \"AES\"\n    private const val cipherTran = \"AES/CBC/PKCS5Padding\"\n    @JvmStatic\n    @Throws(Exception::class)\n    fun dec(encrypted: String?, keyValue: ByteArray?): ByteArray {\n        return try {\n            val cipher = Cipher.getInstance(cipherTran)\n            val secretKey = SecretKeySpec(keyValue, cipherAlgo)\n            cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(ByteArray(16)))\n            cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT or Base64.NO_PADDING or Base64.NO_WRAP))\n        } catch (e: NoSuchAlgorithmException) {\n            throw IncorrectPasswordException(e)\n        } catch (e: NoSuchPaddingException) {\n            throw IncorrectPasswordException(e)\n        } catch (e: InvalidAlgorithmParameterException) {\n            throw IncorrectPasswordException(e)\n        } catch (e: InvalidKeyException) {\n            throw IncorrectPasswordException(e)\n        } catch (e: BadPaddingException) {\n            throw IncorrectPasswordException(e)\n        } catch (e: IllegalBlockSizeException) {\n            throw IncorrectPasswordException(e)\n        }\n    }\n\n    @JvmStatic\n    @Throws(Exception::class)\n    fun enc(str: String, keyValue: ByteArray?): ByteArray {\n        val cipher = Cipher.getInstance(cipherTran)\n        val secretKey = SecretKeySpec(keyValue, cipherAlgo)\n        cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(ByteArray(16)))\n        val bytes = cipher.doFinal(str.toByteArray())\n        return Base64.encode(bytes, Base64.DEFAULT or Base64.NO_PADDING or Base64.NO_WRAP)\n    }\n\n    class IncorrectPasswordException(e: GeneralSecurityException?) : Exception(e)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/PermissionUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.Manifest.permission\nimport android.content.Context\nimport androidx.core.content.PermissionChecker\nimport awais.instagrabber.utils.PermissionUtils\nimport androidx.core.content.ContextCompat\nimport android.content.pm.PackageManager\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.app.ActivityCompat\nimport androidx.fragment.app.Fragment\n\nobject PermissionUtils {\n    val AUDIO_RECORD_PERMS = arrayOf(permission.RECORD_AUDIO)\n    val ATTACH_MEDIA_PERMS = arrayOf(permission.READ_EXTERNAL_STORAGE)\n    val CAMERA_PERMS = arrayOf(permission.CAMERA)\n    @JvmStatic\n    fun hasAudioRecordPerms(context: Context): Boolean {\n        return PermissionChecker.checkSelfPermission(\n            context,\n            permission.RECORD_AUDIO\n        ) == PermissionChecker.PERMISSION_GRANTED\n    }\n\n    @JvmStatic\n    fun requestAudioRecordPerms(fragment: Fragment, requestCode: Int) {\n        fragment.requestPermissions(AUDIO_RECORD_PERMS, requestCode)\n    }\n\n    @JvmStatic\n    fun hasAttachMediaPerms(context: Context): Boolean {\n        return PermissionChecker.checkSelfPermission(\n            context,\n            permission.READ_EXTERNAL_STORAGE\n        ) == PermissionChecker.PERMISSION_GRANTED\n    }\n\n    @JvmStatic\n    fun requestAttachMediaPerms(fragment: Fragment, requestCode: Int) {\n        fragment.requestPermissions(ATTACH_MEDIA_PERMS, requestCode)\n    }\n\n    fun hasCameraPerms(context: Context?): Boolean {\n        return ContextCompat.checkSelfPermission(\n            context!!,\n            permission.CAMERA\n        ) == PackageManager.PERMISSION_GRANTED\n    }\n\n    fun requestCameraPerms(activity: AppCompatActivity?, requestCode: Int) {\n        ActivityCompat.requestPermissions(activity!!, CAMERA_PERMS, requestCode)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ProcessPhoenix.java",
    "content": "/*\n * Copyright (C) 2014 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage awais.instagrabber.utils;\n\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Process;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;\nimport static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;\n\n/**\n * Process Phoenix facilitates restarting your application process. This should only be used for\n * things like fundamental state changes in your debug builds (e.g., changing from staging to\n * production).\n * <p>\n * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.\n */\npublic final class ProcessPhoenix extends Activity {\n    private static final String KEY_RESTART_INTENTS = \"phoenix_restart_intents\";\n\n    /**\n     * Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}\n     * activity as an intent.\n     * <p>\n     * Behavior of the current process after invoking this method is undefined.\n     */\n    public static void triggerRebirth(Context context) {\n        triggerRebirth(context, getRestartIntent(context));\n    }\n\n    /**\n     * Call to restart the application process using the specified intents.\n     * <p>\n     * Behavior of the current process after invoking this method is undefined.\n     */\n    public static void triggerRebirth(Context context, Intent... nextIntents) {\n        Intent intent = new Intent(context, ProcessPhoenix.class);\n        intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.\n        intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents)));\n        context.startActivity(intent);\n        if (context instanceof Activity) {\n            ((Activity) context).finish();\n        }\n        Runtime.getRuntime().exit(0); // Kill kill kill!\n    }\n\n    private static Intent getRestartIntent(Context context) {\n        String packageName = context.getPackageName();\n        Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);\n        if (defaultIntent != null) {\n            defaultIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);\n            return defaultIntent;\n        }\n\n        throw new IllegalStateException(\"Unable to determine default activity for \"\n                                                + packageName\n                                                + \". Does an activity specify the DEFAULT category in its intent filter?\");\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);\n        startActivities(intents.toArray(new Intent[intents.size()]));\n        finish();\n        Runtime.getRuntime().exit(0); // Kill kill kill!\n    }\n\n    /**\n     * Checks if the current process is a temporary Phoenix Process.\n     * This can be used to avoid initialisation of unused resources or to prevent running code that\n     * is not multi-process ready.\n     *\n     * @return true if the current process is a temporary Phoenix Process\n     */\n    public static boolean isPhoenixProcess(Context context) {\n        int currentPid = Process.myPid();\n        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();\n        if (runningProcesses != null) {\n            for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {\n                if (processInfo.pid == currentPid && processInfo.processName.endsWith(\":phoenix\")) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/RankedRecipientsCache.kt",
    "content": "package awais.instagrabber.utils\n\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse\nimport java.time.LocalDateTime\nimport java.time.temporal.ChronoUnit\n\nobject RankedRecipientsCache {\n    private var lastUpdatedOn: LocalDateTime? = null\n    var isUpdateInitiated = false\n    var isFailed = false\n    val rankedRecipients: List<RankedRecipient>\n        get() = response?.rankedRecipients ?: emptyList()\n\n    var response: RankedRecipientsResponse? = null\n        set(value) {\n            field = value\n            lastUpdatedOn = LocalDateTime.now()\n        }\n\n    val isExpired: Boolean\n        get() {\n            if (lastUpdatedOn == null || response == null) return true\n            val expiresInSecs = response!!.expires\n            return LocalDateTime.now().isAfter(lastUpdatedOn!!.plus(expiresInSecs, ChronoUnit.SECONDS))\n        }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.net.Uri;\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.models.enums.MediaItemType;\nimport awais.instagrabber.repositories.responses.Caption;\nimport awais.instagrabber.repositories.responses.FriendshipStatus;\nimport awais.instagrabber.repositories.responses.ImageVersions2;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.MediaCandidate;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.stories.StoryMedia;\n\npublic final class ResponseBodyUtils {\n    private static final String TAG = \"ResponseBodyUtils\";\n\n    // isI: true if the content was requested from i.instagram.com instead of graphql\n    @Nullable\n    public static String getHighQualityPost(final JSONArray resources, final boolean isVideo, final boolean isI, final boolean low) {\n        try {\n            final int resourcesLen = resources.length();\n\n            final String[] sources = new String[resourcesLen];\n            int lastResMain = low ? 1000000 : 0, lastIndexMain = -1;\n            int lastResBase = low ? 1000000 : 0, lastIndexBase = -1;\n            for (int i = 0; i < resourcesLen; ++i) {\n                final JSONObject item = resources.getJSONObject(i);\n                if (item != null && (!isVideo || item.has(Constants.EXTRAS_PROFILE) || isI)) {\n                    sources[i] = item.getString(isI ? \"url\" : \"src\");\n                    final int currRes = item.getInt(isI ? \"width\" : \"config_width\") * item.getInt(isI ? \"height\" : \"config_height\");\n\n                    final String profile = isVideo ? item.optString(Constants.EXTRAS_PROFILE) : null;\n\n                    if (!isVideo || \"MAIN\".equals(profile)) {\n                        if (currRes > lastResMain && !low) {\n                            lastResMain = currRes;\n                            lastIndexMain = i;\n                        } else if (currRes < lastResMain && low) {\n                            lastResMain = currRes;\n                            lastIndexMain = i;\n                        }\n                    } else {\n                        if (currRes > lastResBase && !low) {\n                            lastResBase = currRes;\n                            lastIndexBase = i;\n                        } else if (currRes < lastResBase && low) {\n                            lastResBase = currRes;\n                            lastIndexBase = i;\n                        }\n                    }\n                }\n            }\n\n            if (lastIndexMain >= 0) return sources[lastIndexMain];\n            else if (lastIndexBase >= 0) return sources[lastIndexBase];\n        } catch (final Exception e) {\n            Log.e(TAG, \"\", e);\n        }\n        return null;\n    }\n\n    public static String getHighQualityImage(final JSONObject resources) {\n        String src = null;\n        try {\n            if (resources.has(\"display_resources\"))\n                src = getHighQualityPost(resources.getJSONArray(\"display_resources\"), false, false, false);\n            else if (resources.has(\"image_versions2\"))\n                src = getHighQualityPost(resources.getJSONObject(\"image_versions2\").getJSONArray(\"candidates\"), false, true, false);\n            if (src == null) return resources.getString(\"display_url\");\n        } catch (final Exception e) {\n            Log.e(TAG, \"\", e);\n        }\n        return src;\n    }\n\n    // the \"user\" argument can be null, it's used because instagram redacts user details from responses\n    public static Media parseGraphQLItem(final JSONObject itemJson, final User backup) throws JSONException {\n        if (itemJson == null) {\n            return null;\n        }\n        final JSONObject feedItem = itemJson.has(\"node\") ? itemJson.getJSONObject(\"node\") : itemJson;\n        final String mediaType = feedItem.optString(\"__typename\");\n        if (\"GraphSuggestedUserFeedUnit\".equals(mediaType)) return null;\n\n        final boolean isVideo = feedItem.optBoolean(\"is_video\");\n        final long videoViews = feedItem.optLong(\"video_view_count\", 0);\n\n        final String displayUrl = feedItem.optString(\"display_url\");\n        if (TextUtils.isEmpty(displayUrl)) return null;\n        final String resourceUrl;\n        if (isVideo && feedItem.has(\"video_url\")) {\n            resourceUrl = feedItem.getString(\"video_url\");\n        } else {\n            resourceUrl = feedItem.has(\"display_resources\") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;\n        }\n        JSONObject tempJsonObject = feedItem.optJSONObject(\"edge_media_preview_comment\");\n        final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong(\"count\") : 0;\n        tempJsonObject = feedItem.optJSONObject(\"edge_media_preview_like\");\n        final long likesCount = tempJsonObject != null ? tempJsonObject.optLong(\"count\") : 0;\n        tempJsonObject = feedItem.optJSONObject(\"edge_media_to_caption\");\n        final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray(\"edges\") : null;\n        String captionText = null;\n        if (captions != null && captions.length() > 0) {\n            if ((tempJsonObject = captions.optJSONObject(0)) != null &&\n                    (tempJsonObject = tempJsonObject.optJSONObject(\"node\")) != null) {\n                captionText = tempJsonObject.getString(\"text\");\n            }\n        }\n        final JSONObject locationJson = feedItem.optJSONObject(\"location\");\n        // Log.d(TAG, \"location: \" + (location == null ? null : location.toString()));\n        long locationId = 0;\n        String locationName = null;\n        if (locationJson != null) {\n            locationName = locationJson.optString(\"name\");\n            if (locationJson.has(\"id\")) {\n                locationId = locationJson.optLong(\"id\");\n            } else if (locationJson.has(\"pk\")) {\n                locationId = locationJson.optLong(\"pk\");\n            }\n            // Log.d(TAG, \"locationId: \" + locationId);\n        }\n        int height = 0;\n        int width = 0;\n        final JSONObject dimensions = feedItem.optJSONObject(\"dimensions\");\n        if (dimensions != null) {\n            height = dimensions.optInt(\"height\");\n            width = dimensions.optInt(\"width\");\n        }\n        String thumbnailUrl = null;\n        final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>();\n        if (feedItem.has(\"display_resources\") || feedItem.has(\"thumbnail_resources\")) {\n            final JSONArray displayResources = feedItem.has(\"display_resources\")\n                                               ? feedItem.getJSONArray(\"display_resources\")\n                                               : feedItem.getJSONArray(\"thumbnail_resources\");\n            for (int i = 0; i < displayResources.length(); i++) {\n                final JSONObject displayResource = displayResources.getJSONObject(i);\n                candidates.add(new MediaCandidate(\n                        displayResource.getInt(\"config_width\"),\n                        displayResource.getInt(\"config_height\"),\n                        displayResource.getString(\"src\")\n                ));\n            }\n        }\n        final ImageVersions2 imageVersions2 = new ImageVersions2(candidates);\n\n        User user = backup;\n        long userId = -1;\n        if (feedItem.has(\"owner\") && user == null) {\n            final JSONObject owner = feedItem.getJSONObject(\"owner\");\n            final FriendshipStatus friendshipStatus = new FriendshipStatus(\n                    false,\n                    false,\n                    false,\n                    false,\n                    false,\n                    false,\n                    false,\n                    false,\n                    false,\n                    false\n            );\n            userId = owner.optLong(Constants.EXTRAS_ID, -1);\n            user = new User(\n                    userId,\n                    owner.optString(Constants.EXTRAS_USERNAME),\n                    owner.optString(\"full_name\"),\n                    false,\n                    owner.optString(\"profile_pic_url\"),\n                    owner.optBoolean(\"is_verified\"));\n        }\n        final String id = feedItem.getString(Constants.EXTRAS_ID);\n        MediaCandidate videoVersion = null;\n        if (isVideo) {\n            videoVersion = new MediaCandidate(\n                    width,\n                    height,\n                    resourceUrl\n            );\n        }\n        final Caption caption = new Caption(\n                userId,\n                captionText != null ? captionText : \"\"\n        );\n\n        final boolean isSlider = \"GraphSidecar\".equals(mediaType) && feedItem.has(\"edge_sidecar_to_children\");\n        List<Media> childItems = null;\n        if (isSlider) {\n            childItems = new ArrayList<>();\n            // feedModelBuilder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);\n            final JSONObject sidecar = feedItem.optJSONObject(\"edge_sidecar_to_children\");\n            if (sidecar != null) {\n                final JSONArray children = sidecar.optJSONArray(\"edges\");\n                if (children != null) {\n                    // final List<PostChild> sliderItems = getSliderItems(children);\n                    // feedModelBuilder.setSliderItems(sliderItems)\n                    //                 .setImageHeight(sliderItems.get(0).getHeight())\n                    //                 .setImageWidth(sliderItems.get(0).getWidth());\n                    for (int i = 0; i < children.length(); i++) {\n                        final JSONObject child = children.optJSONObject(i);\n                        if (child == null) continue;\n                        final Media media = parseGraphQLItem(child, null);\n                        media.setSidecarChild(true);\n                        childItems.add(media);\n                    }\n                }\n            }\n        }\n        MediaItemType mediaItemType = MediaItemType.MEDIA_TYPE_IMAGE;\n        if (isSlider) {\n            mediaItemType = MediaItemType.MEDIA_TYPE_SLIDER;\n        } else if (isVideo) {\n            mediaItemType = MediaItemType.MEDIA_TYPE_VIDEO;\n        }\n        final Location location = new Location(\n                locationId,\n                locationName,\n                locationName,\n                null,\n                null,\n                -1,\n                -1\n        );\n        return new Media(\n                id,\n                id,\n                feedItem.optString(Constants.EXTRAS_SHORTCODE),\n                feedItem.optLong(\"taken_at_timestamp\", -1),\n                user,\n                false,\n                imageVersions2,\n                width,\n                height,\n                mediaItemType.getId(),\n                false,\n                feedItem.optBoolean(\"comments_disabled\"),\n                -1,\n                commentsCount,\n                likesCount,\n                false,\n                false,\n                isVideo ? Collections.singletonList(videoVersion) : null,\n                feedItem.optBoolean(\"has_audio\"),\n                feedItem.optDouble(\"video_duration\"),\n                videoViews,\n                caption,\n                false,\n                null,\n                null,\n                childItems,\n                location,\n                null,\n                false,\n                false,\n                null,\n                null,\n                null\n        );\n    }\n\n    public static String getThumbUrl(final Object media) {\n        return getImageCandidate(media, CandidateType.THUMBNAIL);\n    }\n\n    public static String getImageUrl(final Object media) {\n        return getImageCandidate(media, CandidateType.DOWNLOAD);\n    }\n\n    private static String getImageCandidate(final Object rawMedia, final CandidateType type) {\n        if (rawMedia == null) return null;\n        final ImageVersions2 imageVersions2;\n        final int originalWidth, originalHeight;\n        if (rawMedia instanceof StoryMedia) {\n            imageVersions2 = ((StoryMedia) rawMedia).getImageVersions2();\n            originalWidth = ((StoryMedia) rawMedia).getOriginalWidth();\n            originalHeight = ((StoryMedia) rawMedia).getOriginalHeight();\n        }\n        else if (rawMedia instanceof Media) {\n            imageVersions2 = ((Media) rawMedia).getImageVersions2();\n            originalWidth = ((Media) rawMedia).getOriginalWidth();\n            originalHeight = ((Media) rawMedia).getOriginalHeight();\n        }\n        else return null;\n        if (imageVersions2 == null) return null;\n        final List<MediaCandidate> candidates = imageVersions2.getCandidates();\n        if (candidates == null || candidates.isEmpty()) return null;\n        final boolean isSquare = Integer.compare(originalWidth, originalHeight) == 0;\n        final List<MediaCandidate> sortedCandidates = candidates.stream()\n                                                                .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth()))\n                                                                .collect(Collectors.toList());\n        final List<MediaCandidate> filteredCandidates = sortedCandidates.stream()\n                                                                        .filter(c ->\n                                                                                        c.getWidth() <= originalWidth\n                                                                                                && c.getWidth() <= type.getValue()\n                                                                                                && (isSquare || Integer\n                                                                                                .compare(c.getWidth(), c.getHeight()) != 0)\n                                                                        )\n                                                                        .collect(Collectors.toList());\n        if (filteredCandidates.size() == 0) return sortedCandidates.get(0).getUrl();\n        final MediaCandidate candidate = filteredCandidates.get(0);\n        if (candidate == null) return null;\n        return candidate.getUrl();\n    }\n\n    public static String getThumbVideoUrl(final Media media) {\n        return getVideoCandidate(media, CandidateType.VIDEO_THUMBNAIL);\n    }\n\n    public static String getVideoUrl(final Object media) {\n        return getVideoCandidate(media, CandidateType.DOWNLOAD);\n    }\n\n    // TODO: merge with getImageCandidate when Kotlin\n    private static String getVideoCandidate(final Object rawMedia, final CandidateType type) {\n        if (rawMedia == null) return null;\n        final List<MediaCandidate> candidates;\n        final int originalWidth, originalHeight;\n        if (rawMedia instanceof StoryMedia) {\n            candidates = ((StoryMedia) rawMedia).getVideoVersions();\n            originalWidth = ((StoryMedia) rawMedia).getOriginalWidth();\n            originalHeight = ((StoryMedia) rawMedia).getOriginalHeight();\n        }\n        else if (rawMedia instanceof Media) {\n            candidates = ((Media) rawMedia).getVideoVersions();\n            originalWidth = ((Media) rawMedia).getOriginalWidth();\n            originalHeight = ((Media) rawMedia).getOriginalHeight();\n        }\n        else return null;\n        if (candidates == null || candidates.isEmpty()) return null;\n        final boolean isSquare = Integer.compare(originalWidth, originalHeight) == 0;\n        final List<MediaCandidate> sortedCandidates = candidates.stream()\n                                                                .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth()))\n                                                                .collect(Collectors.toList());\n        final List<MediaCandidate> filteredCandidates = sortedCandidates.stream()\n                                                                        .filter(c ->\n                                                                                        c.getWidth() <= originalWidth\n                                                                                                && c.getWidth() <= type.getValue()\n                                                                                                && (isSquare || Integer\n                                                                                                .compare(c.getWidth(), c.getHeight()) != 0)\n                                                                        )\n                                                                        .collect(Collectors.toList());\n        if (filteredCandidates.size() == 0) return sortedCandidates.get(0).getUrl();\n        final MediaCandidate candidate = filteredCandidates.get(0);\n        if (candidate == null) return null;\n        return candidate.getUrl();\n    }\n\n    private enum CandidateType {\n        VIDEO_THUMBNAIL(700),\n        THUMBNAIL(1000),\n        DOWNLOAD(10000);\n\n        private final int value;\n\n        CandidateType(final int value) {\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/SerializablePair.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.util.Pair\nimport java.io.Serializable\n\n/**\n * Constructor for a Pair.\n *\n * @param first  the first object in the Pair\n * @param second the second object in the pair\n */\ndata class SerializablePair<F, S>(@JvmField val first: F, @JvmField val second: S) : Pair<F, S>(first, second), Serializable"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/SettingsHelper.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.SharedPreferences\nimport android.os.Build\nimport androidx.annotation.StringDef\nimport androidx.appcompat.app.AppCompatDelegate\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport java.util.*\n\nclass SettingsHelper(context: Context) {\n    private val sharedPreferences: SharedPreferences? = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)\n\n    fun getString(@StringSettings key: String): String {\n        val stringDefault = getStringDefault(key)\n        return sharedPreferences?.getString(\n            key,\n            stringDefault\n        ) ?: stringDefault\n    }\n\n    fun getStringSet(@StringSetSettings key: String?): Set<String> {\n        val stringSetDefault: Set<String> = HashSet()\n        return sharedPreferences?.getStringSet(\n            key,\n            stringSetDefault\n        ) ?: stringSetDefault\n    }\n\n    fun getInteger(@IntegerSettings key: String): Int {\n        val integerDefault = getIntegerDefault(key)\n        return sharedPreferences?.getInt(key, integerDefault) ?: integerDefault\n    }\n\n    fun getBoolean(@BooleanSettings key: String?): Boolean {\n        return sharedPreferences?.getBoolean(key, false) ?: false\n    }\n\n    private fun getStringDefault(@StringSettings key: String): String {\n        if (PreferenceKeys.DATE_TIME_FORMAT == key) {\n            return Constants.defaultDateTimeFormat\n        }\n        return if (PreferenceKeys.DATE_TIME_SELECTION == key) \"0;3;0\" else \"\"\n    }\n\n    private fun getIntegerDefault(@IntegerSettings key: String): Int {\n        if (PreferenceKeys.APP_THEME == key) return getThemeCode(true)\n        return if (Constants.PREV_INSTALL_VERSION == key || Constants.APP_UA_CODE == key || Constants.BROWSER_UA_CODE == key) -1 else 0\n    }\n\n    fun getThemeCode(fromHelper: Boolean): Int {\n        var themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM\n        if (!fromHelper && sharedPreferences != null) {\n            themeCode = sharedPreferences.getString(PreferenceKeys.APP_THEME, themeCode.toString())?.toInt() ?: 0\n            when (themeCode) {\n                1 -> themeCode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY\n                3 -> themeCode = AppCompatDelegate.MODE_NIGHT_NO\n                0 -> themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM\n            }\n        }\n        if (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM && Build.VERSION.SDK_INT < 29) {\n            themeCode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY\n        }\n        return themeCode\n    }\n\n    fun putString(@StringSettings key: String?, `val`: String?) {\n        sharedPreferences?.edit()?.putString(key, `val`)?.apply()\n    }\n\n    fun putStringSet(@StringSetSettings key: String?, `val`: Set<String?>?) {\n        sharedPreferences?.edit()?.putStringSet(key, `val`)?.apply()\n    }\n\n    fun putInteger(@IntegerSettings key: String?, `val`: Int) {\n        sharedPreferences?.edit()?.putInt(key, `val`)?.apply()\n    }\n\n    fun putBoolean(@BooleanSettings key: String?, `val`: Boolean) {\n        sharedPreferences?.edit()?.putBoolean(key, `val`)?.apply()\n    }\n\n    fun hasPreference(key: String?): Boolean {\n        return sharedPreferences?.contains(key) ?: false\n    }\n\n    @StringDef(\n        PreferenceKeys.APP_LANGUAGE,\n        PreferenceKeys.APP_THEME,\n        Constants.APP_UA,\n        Constants.BROWSER_UA,\n        Constants.COOKIE,\n        PreferenceKeys.FOLDER_PATH,\n        PreferenceKeys.DATE_TIME_FORMAT,\n        PreferenceKeys.DATE_TIME_SELECTION,\n        PreferenceKeys.CUSTOM_DATE_TIME_FORMAT,\n        Constants.DEVICE_UUID,\n        Constants.SKIPPED_VERSION,\n        Constants.DEFAULT_TAB,\n        Constants.PREF_DARK_THEME,\n        Constants.PREF_LIGHT_THEME,\n        Constants.PREF_POSTS_LAYOUT,\n        Constants.PREF_PROFILE_POSTS_LAYOUT,\n        Constants.PREF_TOPIC_POSTS_LAYOUT,\n        Constants.PREF_HASHTAG_POSTS_LAYOUT,\n        Constants.PREF_LOCATION_POSTS_LAYOUT,\n        Constants.PREF_LIKED_POSTS_LAYOUT,\n        Constants.PREF_TAGGED_POSTS_LAYOUT,\n        Constants.PREF_SAVED_POSTS_LAYOUT,\n        PreferenceKeys.STORY_SORT,\n        Constants.PREF_EMOJI_VARIANTS,\n        Constants.PREF_REACTIONS,\n        PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT,\n        PreferenceKeys.PREF_TAB_ORDER,\n        PreferenceKeys.PREF_BARINSTA_DIR_URI\n    )\n    annotation class StringSettings\n\n    @StringDef(\n        PreferenceKeys.DOWNLOAD_USER_FOLDER,\n        PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME,\n        PreferenceKeys.AUTOPLAY_VIDEOS_STORIES,\n        PreferenceKeys.MUTED_VIDEOS,\n//        PreferenceKeys.SHOW_CAPTIONS,\n        PreferenceKeys.CUSTOM_DATE_TIME_FORMAT_ENABLED,\n        PreferenceKeys.MARK_AS_SEEN,\n        PreferenceKeys.DM_MARK_AS_SEEN,\n        PreferenceKeys.CHECK_ACTIVITY,\n        PreferenceKeys.CHECK_UPDATES,\n        PreferenceKeys.SWAP_DATE_TIME_FORMAT_ENABLED,\n        PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS,\n        PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH,\n        PreferenceKeys.FLAG_SECURE,\n        PreferenceKeys.TOGGLE_KEYWORD_FILTER,\n        PreferenceKeys.PREF_ENABLE_SENTRY,\n        PreferenceKeys.HIDE_MUTED_REELS,\n        PreferenceKeys.PLAY_IN_BACKGROUND,\n        PreferenceKeys.PREF_SHOWN_COUNT_TOOLTIP,\n        PreferenceKeys.PREF_SEARCH_FOCUS_KEYBOARD,\n        PreferenceKeys.PREF_STORY_SHOW_LIST,\n        PreferenceKeys.PREF_AUTO_BACKUP_ENABLED\n    )\n    annotation class BooleanSettings\n\n    @StringDef(\n        Constants.PREV_INSTALL_VERSION,\n        Constants.BROWSER_UA_CODE,\n        Constants.APP_UA_CODE,\n        PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER\n    )\n    annotation class IntegerSettings\n\n    @StringDef(PreferenceKeys.KEYWORD_FILTERS)\n    annotation class StringSetSettings\n\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/SingleLiveEvent.kt",
    "content": "/*\n *  Copyright 2017 Google Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage awais.instagrabber.utils\n\nimport android.util.Log\nimport androidx.annotation.MainThread\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Observer\nimport awais.instagrabber.utils.extensions.TAG\nimport java.util.concurrent.atomic.AtomicBoolean\n\n/**\n * A lifecycle-aware observable that sends only new updates after subscription, used for events like\n * navigation and Snackbar messages.\n *\n *\n * This avoids a common problem with events: on configuration change (like rotation) an update\n * can be emitted if the observer is active. This LiveData only calls the observable if there's an\n * explicit call to setValue() or call().\n *\n *\n * Note that only one observer is going to be notified of changes.\n */\nclass SingleLiveEvent<T> : MutableLiveData<T>() {\n    private val pending = AtomicBoolean(false)\n\n    @MainThread\n    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {\n        if (hasActiveObservers()) {\n            Log.w(TAG, \"Multiple observers registered but only one will be notified of changes.\")\n        }\n        // Observe the internal MutableLiveData\n        super.observe(owner, { t ->\n            if (pending.compareAndSet(true, false)) {\n                observer.onChanged(t)\n            }\n        })\n    }\n\n    @MainThread\n    override fun setValue(t: T?) {\n        pending.set(true)\n        super.setValue(t)\n    }\n\n    /**\n     * Used for cases where T is Void, to make calls cleaner.\n     */\n    @MainThread\n    fun call() {\n        value = null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/SingletonHolder.kt",
    "content": "package awais.instagrabber.utils\n\nopen class SingletonHolder<out T : Any, in A>(creator: (A) -> T) {\n    private var creator: ((A) -> T)? = creator\n\n    @Volatile\n    private var instance: T? = null\n\n    fun getInstance(arg: A): T {\n        val i = instance\n        if (i != null) {\n            return i\n        }\n\n        return synchronized(this) {\n            val i2 = instance\n            if (i2 != null) {\n                i2\n            } else {\n                val created = creator!!(arg)\n                instance = created\n                creator = null\n                created\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/TextUtils.kt",
    "content": "package awais.instagrabber.utils\n\nimport android.util.Patterns\nimport java.time.Duration\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneId\nimport java.time.format.DateTimeFormatter\nimport java.time.format.FormatStyle\nimport java.util.*\nimport kotlin.math.absoluteValue\n\nobject TextUtils {\n    var dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern(Constants.defaultDateTimeFormat)\n\n    @JvmStatic\n    fun isEmpty(charSequence: CharSequence?): Boolean {\n        if (charSequence.isNullOrBlank()) return true\n        if (charSequence is String) {\n            var str = charSequence\n            if (\"\" == str || \"null\" == str || str.isEmpty()) return true\n            str = str.trim { it <= ' ' }\n            return \"\" == str || \"null\" == str || str.isEmpty()\n        }\n        return \"null\".contentEquals(charSequence) || \"\".contentEquals(charSequence)\n    }\n\n    @JvmStatic\n    @JvmOverloads\n    fun millisToTimeString(millis: Long, includeHoursAlways: Boolean = false): String {\n        val sec = (millis / 1000).toInt() % 60\n        var min = (millis / (1000 * 60)).toInt()\n        if (min >= 60) {\n            min = (millis / (1000 * 60) % 60).toInt()\n            val hr = (millis / (1000 * 60 * 60) % 24).toInt()\n            return String.format(Locale.ENGLISH, \"%02d:%02d:%02d\", hr, min, sec)\n        }\n        return if (includeHoursAlways) {\n            String.format(Locale.ENGLISH, \"%02d:%02d:%02d\", 0, min, sec)\n        } else String.format(Locale.ENGLISH, \"%02d:%02d\", min, sec)\n    }\n\n    private val timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)\n    private val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)\n\n    @JvmStatic\n    fun getRelativeDateTimeString(from: Long): String {\n        val now = LocalDateTime.now()\n        val then = LocalDateTime.ofInstant(Instant.ofEpochMilli(from), ZoneId.systemDefault())\n        val days = Duration.between(now, then).toDays().absoluteValue\n        return then.format(if (days == 0L) timeFormatter else dateFormatter)\n    }\n\n    @JvmStatic\n    fun extractUrls(text: String): List<String> {\n        if (isEmpty(text)) return emptyList()\n        val matcher = Patterns.WEB_URL.matcher(text)\n        val urls: MutableList<String> = ArrayList()\n        while (matcher.find()) {\n            urls.add(matcher.group())\n        }\n        return urls\n    }\n\n    // https://github.com/notslang/instagram-id-to-url-segment\n    @JvmStatic\n    fun shortcodeToId(shortcode: String): Long {\n        var result = 0L\n        var i = 0\n        while (i < shortcode.length && i < 11) {\n            val c = shortcode[i]\n            val k = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\".indexOf(c)\n            result = result * 64 + k\n            i++\n        }\n        return result\n    }\n\n    @JvmStatic\n    fun setFormatter(datetimeParser: DateTimeFormatter) {\n        if (!DateUtils.checkFormatterValid(datetimeParser)) return\n        this.dateTimeFormatter = datetimeParser\n    }\n\n    @JvmStatic\n    fun epochSecondToString(epochSecond: Long): String {\n        return LocalDateTime.ofInstant(\n            Instant.ofEpochSecond(epochSecond),\n            ZoneId.systemDefault()\n        ).format(dateTimeFormatter)\n    }\n\n    @JvmStatic\n    fun nowToString(): String {\n        return LocalDateTime.now().format(dateTimeFormatter)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ThemeUtils.kt",
    "content": "@file:JvmName(\"ThemeUtils\")\n\npackage awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.res.Configuration\nimport android.os.Build\nimport androidx.appcompat.app.AppCompatDelegate\nimport awais.instagrabber.R\n\nobject ThemeUtils {\n    fun changeTheme(context: Context) {\n        var themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM // this is fallback / default\n        if (Utils.settingsHelper != null) themeCode = Utils.settingsHelper.getThemeCode(false)\n        if (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM && Build.VERSION.SDK_INT < 29) {\n            themeCode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY\n        }\n        val isNight = isNight(context, themeCode)\n        val themeResName =\n            if (isNight) Utils.settingsHelper.getString(Constants.PREF_DARK_THEME) else Utils.settingsHelper.getString(\n                Constants.PREF_LIGHT_THEME\n            )\n        val themeResId = context.resources.getIdentifier(themeResName, \"style\", context.packageName)\n        val finalThemeResId: Int\n        finalThemeResId = if (themeResId <= 0) {\n            // Nothing set in settings\n            if (isNight) R.style.AppTheme_Dark_Black else R.style.AppTheme_Light_White\n        } else themeResId\n        // Log.d(TAG, \"changeTheme: finalThemeResId: \" + finalThemeResId);\n        context.setTheme(finalThemeResId)\n    }\n\n    fun isNight(context: Context, themeCode: Int): Boolean {\n        // check if setting is set to 'Dark'\n        var isNight = themeCode == AppCompatDelegate.MODE_NIGHT_YES\n        // if not dark check if themeCode is MODE_NIGHT_FOLLOW_SYSTEM or MODE_NIGHT_AUTO_BATTERY\n        if (!isNight && (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM || themeCode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)) {\n            // check if resulting theme would be NIGHT\n            val uiMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK\n            isNight = uiMode == Configuration.UI_MODE_NIGHT_YES\n        }\n        return isNight\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.kt",
    "content": "@file:JvmName(\"UpdateCheckCommon\")\n\npackage awais.instagrabber.utils\n\nimport android.content.Context\nimport android.content.DialogInterface\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.R\nimport awais.instagrabber.utils.AppExecutors.mainThread\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\n\nfun shouldShowUpdateDialog(\n    force: Boolean,\n    version: String\n): Boolean {\n    val skippedVersion = Utils.settingsHelper.getString(Constants.SKIPPED_VERSION)\n    return force || !BuildConfig.DEBUG && skippedVersion != version\n}\n\nfun showUpdateDialog(\n    context: Context,\n    version: String,\n    onDownloadClickListener: DialogInterface.OnClickListener\n) {\n    mainThread.execute {\n        MaterialAlertDialogBuilder(context).apply {\n            setTitle(context.getString(R.string.update_available, version))\n            setNeutralButton(R.string.skip_update) { dialog: DialogInterface, _: Int ->\n                Utils.settingsHelper.putString(Constants.SKIPPED_VERSION, version)\n                dialog.dismiss()\n            }\n            setPositiveButton(R.string.action_download, onDownloadClickListener)\n            setNegativeButton(R.string.cancel, null)\n        }.show()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/UserAgentUtils.kt",
    "content": "@file:JvmName(\"UserAgentUtils\")\n\npackage awais.instagrabber.utils\n\n/* GraphQL user agents (which are just standard browser UA\"s).\n * Go to https://www.whatismybrowser.com/guides/the-latest-user-agent/ to update it\n * Windows first (Assume win64 not wow64): Chrome, Firefox, Edge\n * Then macOS: Chrome, Firefox, Safari\n */\n@JvmField\nval browsers = arrayOf(\n    \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\",\n    \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0\",\n    \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62\",\n    \"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\",\n    \"Mozilla/5.0 (Macintosh; Intel Mac OS X 11.3; rv:88.0) Gecko/20100101 Firefox/88.0\",\n    \"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15\"\n)\n\n// use APKpure, assume arm64-v8a\nprivate const val igVersion = \"195.0.0.31.123\"\nprivate const val igVersionCode = \"302733772\"\n\n// you can pick *any* device as long as you LEAVE OUT the resolution for maximum download quality\n// https://github.com/dilame/instagram-private-api/blob/master/src/samples/devices.json\n@JvmField\nval devices = arrayOf(\n    \"25/7.1.1; 440dpi; 2880x5884; Xiaomi; Mi Note 3; jason; qcom\",\n    \"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi Note 3; kenzo; qcom\",\n    \"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4; nikel; mt6797\",\n    \"24/7.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 4; mido; qcom\",\n    \"23/6.0; 480dpi; 2880x5884; Xiaomi; Redmi Note 4X; nikel; mt6797\",\n    \"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi Note 5; whyred; qcom\",\n    \"23/6.0.1; 480dpi; 2880x5884; Xiaomi; Redmi 4; markw; qcom\",\n    \"27/8.1.0; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom\",\n    \"25/7.1.2; 440dpi; 2880x5884; Xiaomi/xiaomi; Redmi 5 Plus; vince; qcom\",\n    \"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 5; gemini; qcom\",\n    \"27/8.1.0; 480dpi; 2880x5884; Xiaomi/xiaomi; Mi A1; tissot_sprout; qcom\",\n    \"26/8.0.0; 480dpi; 2880x5884; Xiaomi; MI 6; sagit; qcom\",\n    \"25/7.1.1; 440dpi; 2880x5884; Xiaomi; MI MAX 2; oxygen; qcom\",\n    \"24/7.0; 480dpi; 2880x5884; Xiaomi; MI 5s; capricorn; qcom\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A520F; a5y17lte; samsungexynos7880\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895\",\n    \"26/8.0.0; 640dpi; 2880x5884; samsung; SM-G950F; dreamlte; samsungexynos8895\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895\",\n    \"26/8.0.0; 560dpi; 2880x5884; samsung; SM-G955F; dream2lte; samsungexynos8895\",\n    \"24/7.0; 480dpi; 2880x5884; samsung; SM-A510F; a5xelte; samsungexynos7580\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-G965F; star2lte; samsungexynos9810\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-A530F; jackpotlte; samsungexynos7885\",\n    \"24/7.0; 640dpi; 2880x5884; samsung; SM-G925F; zerolte; samsungexynos7420\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A720F; a7y17lte; samsungexynos7880\",\n    \"24/7.0; 640dpi; 2880x5884; samsung; SM-G920F; zeroflte; samsungexynos7420\",\n    \"24/7.0; 420dpi; 2880x5884; samsung; SM-J730FM; j7y17lte; samsungexynos7870\",\n    \"26/8.0.0; 480dpi; 2880x5884; samsung; SM-G960F; starlte; samsungexynos9810\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-N950F; greatlte; samsungexynos8895\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A730F; jackpot2lte; samsungexynos7885\",\n    \"26/8.0.0; 420dpi; 2880x5884; samsung; SM-A605FN; a6plte; qcom\",\n    \"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; STF-L09; HWSTF; hi3660\",\n    \"27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970\",\n    \"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; LLD-L31; HWLLD-H; hi6250\",\n    \"26/8.0.0; 480dpi; 2880x5884; HUAWEI; ANE-LX1; HWANE; hi6250\",\n    \"26/8.0.0; 480dpi; 2880x5884; HUAWEI; FIG-LX1; HWFIG-H; hi6250\",\n    \"27/8.1.0; 480dpi; 2880x5884; HUAWEI/HONOR; COL-L29; HWCOL; kirin970\",\n    \"26/8.0.0; 480dpi; 2880x5884; HUAWEI/HONOR; BND-L21; HWBND-H; hi6250\",\n    \"23/6.0.1; 420dpi; 2880x5884; LeMobile/LeEco; Le X527; le_s2_ww; qcom\",  // https://github.com/mimmi20/BrowserDetector/tree/master\n    \"28/9; 560dpi; 2880x5884; samsung; SM-N960F; crownlte; samsungexynos9810\",  // mgp25\n    \"23/6.0.1; 640dpi; 2880x5884; LGE/lge; RS988; h1; h1\",\n    \"24/7.0; 640dpi; 2880x5884; HUAWEI; LON-L29; HWLON; hi3660\",\n    \"23/6.0.1; 640dpi; 2880x5884; ZTE; ZTE A2017U; ailsa_ii; qcom\",\n    \"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G935F; hero2lte; samsungexynos8890\",\n    \"23/6.0.1; 640dpi; 2880x5884; samsung; SM-G930F; herolte; samsungexynos8890\"\n)\n\nfun generateBrowserUA(code: Int): String {\n    return browsers[code]\n}\n\nfun generateAppUA(code: Int, lang: String): String {\n    return \"Instagram \" + igVersion + \" Android (\" + devices[code] + \"; \" + lang + \"; \" + igVersionCode + \")\"\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/Utils.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.ClipData;\nimport android.content.ClipboardManager;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Resources;\nimport android.graphics.Color;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.storage.StorageManager;\nimport android.provider.Browser;\nimport android.provider.DocumentsContract;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.util.TypedValue;\nimport android.view.Display;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.view.inputmethod.InputMethodManager;\nimport android.webkit.MimeTypeMap;\nimport android.widget.Toast;\n\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.content.ContextCompat;\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MediatorLiveData;\nimport androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;\n\nimport com.google.android.exoplayer2.database.ExoDatabaseProvider;\nimport com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;\nimport com.google.android.exoplayer2.upstream.cache.SimpleCache;\n\nimport org.json.JSONObject;\n\nimport java.io.File;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.models.PostsLayoutPreferences;\nimport awais.instagrabber.models.enums.FavoriteType;\n\npublic final class Utils {\n    private static final String TAG = \"Utils\";\n    private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024;\n\n    // public static LogCollector logCollector;\n    public static SettingsHelper settingsHelper;\n    public static boolean sessionVolumeFull = false;\n    public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();\n    public static final DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();\n    public static ClipboardManager clipboardManager;\n    public static SimpleCache simpleCache;\n    private static int statusBarHeight;\n    private static int actionBarHeight;\n    public static String cacheDir;\n    private static int defaultStatusBarColor;\n    private static Object[] volumes;\n\n    public static int convertDpToPx(final float dp) {\n        return Math.round((dp * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);\n    }\n\n    public static void copyText(@NonNull final Context context, final CharSequence string) {\n        if (clipboardManager == null) {\n            clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);\n        }\n        int toastMessage = R.string.clipboard_error;\n        if (clipboardManager != null) {\n            try {\n                clipboardManager.setPrimaryClip(ClipData.newPlainText(context.getString(R.string.app_name), string));\n                toastMessage = R.string.clipboard_copied;\n            } catch (Exception e) {\n                Log.e(TAG, \"copyText: \", e);\n            }\n        }\n        Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();\n    }\n\n    public static Map<String, String> sign(final Map<String, Object> form) {\n        // final String signed = sign(Constants.SIGNATURE_KEY, new JSONObject(form).toString());\n        // if (signed == null) {\n        //     return null;\n        // }\n        final Map<String, String> map = new HashMap<>();\n        // map.put(\"ig_sig_key_version\", Constants.SIGNATURE_VERSION);\n        // map.put(\"signed_body\", signed);\n        map.put(\"signed_body\", \"SIGNATURE.\" + new JSONObject(form).toString());\n        return map;\n    }\n\n    // public static String sign(final String key, final String message) {\n    //     try {\n    //         final Mac hasher = Mac.getInstance(\"HmacSHA256\");\n    //         hasher.init(new SecretKeySpec(key.getBytes(), \"HmacSHA256\"));\n    //         byte[] hash = hasher.doFinal(message.getBytes());\n    //         final StringBuilder hexString = new StringBuilder();\n    //         for (byte b : hash) {\n    //             final String hex = Integer.toHexString(0xff & b);\n    //             if (hex.length() == 1) hexString.append('0');\n    //             hexString.append(hex);\n    //         }\n    //         return hexString.toString() + \".\" + message;\n    //     } catch (Exception e) {\n    //         Log.e(TAG, \"Error signing\", e);\n    //         return null;\n    //     }\n    // }\n\n    public static String getMimeType(@NonNull final Uri uri, final ContentResolver contentResolver) {\n        String mimeType;\n        final String scheme = uri.getScheme();\n        final String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());\n        if (TextUtils.isEmpty(scheme)) {\n            mimeType = mimeTypeMap.getMimeTypeFromExtension(fileExtension.toLowerCase());\n        } else {\n            if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {\n                mimeType = contentResolver.getType(uri);\n            } else {\n                mimeType = mimeTypeMap.getMimeTypeFromExtension(fileExtension.toLowerCase());\n            }\n        }\n        if (mimeType == null) return null;\n        return mimeType.toLowerCase();\n    }\n\n    public static SimpleCache getSimpleCacheInstance(final Context context) {\n        if (context == null) {\n            return null;\n        }\n        final ExoDatabaseProvider exoDatabaseProvider = new ExoDatabaseProvider(context);\n        final File cacheDir = context.getCacheDir();\n        if (simpleCache == null && cacheDir != null) {\n            simpleCache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(VIDEO_CACHE_MAX_BYTES), exoDatabaseProvider);\n        }\n        return simpleCache;\n    }\n\n    @Nullable\n    public static Pair<FavoriteType, String> migrateOldFavQuery(final String queryText) {\n        if (queryText.startsWith(\"@\")) {\n            return new Pair<>(FavoriteType.USER, queryText.substring(1));\n        } else if (queryText.contains(\"/\")) {\n            return new Pair<>(FavoriteType.LOCATION, queryText.substring(0, queryText.indexOf(\"/\")));\n        } else if (queryText.startsWith(\"#\")) {\n            return new Pair<>(FavoriteType.HASHTAG, queryText.substring(1));\n        }\n        return null;\n    }\n\n    public static int getStatusBarHeight(final Context context) {\n        if (statusBarHeight > 0) {\n            return statusBarHeight;\n        }\n        int resourceId = context.getResources().getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);\n        }\n        return statusBarHeight;\n    }\n\n    public static int getActionBarHeight(@NonNull final Context context) {\n        if (actionBarHeight > 0) {\n            return actionBarHeight;\n        }\n        final TypedValue tv = new TypedValue();\n        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {\n            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, displayMetrics);\n        }\n        return actionBarHeight;\n    }\n\n    public static void openURL(final Context context, final String url) {\n        if (context == null || TextUtils.isEmpty(url)) {\n            return;\n        }\n        try {\n            String url1 = url;\n            // add http:// if string doesn't have http:// or https://\n            if (!url.startsWith(\"http://\") && !url.startsWith(\"https://\")) {\n                url1 = \"http://\" + url;\n            }\n            final Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url1));\n            i.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());\n            i.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);\n            context.startActivity(i);\n        } catch (ActivityNotFoundException e) {\n            Log.e(TAG, \"openURL: No activity found to handle URLs\", e);\n            Toast.makeText(context, context.getString(R.string.no_external_app_url), Toast.LENGTH_LONG).show();\n        } catch (Exception e) {\n            Log.e(TAG, \"openURL\", e);\n        }\n    }\n\n    public static void openEmailAddress(final Context context, final String emailAddress) {\n        if (context == null || TextUtils.isEmpty(emailAddress)) {\n            return;\n        }\n        Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(\"mailto:\" + emailAddress));\n        emailIntent.putExtra(Intent.EXTRA_SUBJECT, \"\");\n        emailIntent.putExtra(Intent.EXTRA_TEXT, \"\");\n        context.startActivity(emailIntent);\n    }\n\n    public static void displayToastAboveView(@NonNull final Context context,\n                                             @NonNull final View view,\n                                             @NonNull final String text) {\n        final Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);\n        toast.setGravity(Gravity.TOP | Gravity.START,\n                         view.getLeft(),\n                         view.getTop());\n        toast.show();\n    }\n\n    public static PostsLayoutPreferences getPostsLayoutPreferences(final String layoutPreferenceKey) {\n        PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(layoutPreferenceKey));\n        if (layoutPreferences == null) {\n            layoutPreferences = PostsLayoutPreferences.builder().build();\n            settingsHelper.putString(layoutPreferenceKey, layoutPreferences.getJson());\n        }\n        return layoutPreferences;\n    }\n\n    private static Field mAttachInfoField;\n    private static Field mStableInsetsField;\n\n    public static int getViewInset(View view) {\n        if (view == null\n                || view.getHeight() == displayMetrics.heightPixels\n                || view.getHeight() == displayMetrics.widthPixels - getStatusBarHeight(view.getContext())) {\n            return 0;\n        }\n        try {\n            if (mAttachInfoField == null) {\n                //noinspection JavaReflectionMemberAccess\n                mAttachInfoField = View.class.getDeclaredField(\"mAttachInfo\");\n                mAttachInfoField.setAccessible(true);\n            }\n            Object mAttachInfo = mAttachInfoField.get(view);\n            if (mAttachInfo != null) {\n                if (mStableInsetsField == null) {\n                    mStableInsetsField = mAttachInfo.getClass().getDeclaredField(\"mStableInsets\");\n                    mStableInsetsField.setAccessible(true);\n                }\n                Rect insets = (Rect) mStableInsetsField.get(mAttachInfo);\n                if (insets == null) {\n                    return 0;\n                }\n                return insets.bottom;\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"getViewInset\", e);\n        }\n        return 0;\n    }\n\n    public static int getThemeAccentColor(Context context) {\n        int colorAttr;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            colorAttr = android.R.attr.colorAccent;\n        } else {\n            //Get colorAccent defined for AppCompat\n            colorAttr = context.getResources().getIdentifier(\"colorAccent\", \"attr\", context.getPackageName());\n        }\n        TypedValue outValue = new TypedValue();\n        context.getTheme().resolveAttribute(colorAttr, outValue, true);\n        return outValue.data;\n    }\n\n    public static int getAttrValue(@NonNull final Context context, final int attr) {\n        final TypedValue outValue = new TypedValue();\n        context.getTheme().resolveAttribute(attr, outValue, true);\n        return outValue.data;\n    }\n\n    public static int getAttrResId(@NonNull final Context context, final int attr) {\n        final TypedValue outValue = new TypedValue();\n        context.getTheme().resolveAttribute(attr, outValue, true);\n        return outValue.resourceId;\n    }\n\n    public static void transparentStatusBar(final Activity activity,\n                                            final boolean enable,\n                                            final boolean fullscreen) {\n        if (activity == null) return;\n        final ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();\n        final Window window = activity.getWindow();\n        final View decorView = window.getDecorView();\n        if (enable) {\n            decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);\n            if (actionBar != null) {\n                actionBar.hide();\n            }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                defaultStatusBarColor = window.getStatusBarColor();\n                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n                // FOR TRANSPARENT NAVIGATION BAR\n                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);\n                window.setStatusBarColor(Color.TRANSPARENT);\n                Log.d(TAG, \"Setting Color Transparent \" + Color.TRANSPARENT + \" Default Color \" + defaultStatusBarColor);\n                return;\n            }\n            Log.d(TAG, \"Setting Color Trans \" + Color.TRANSPARENT);\n            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n            return;\n        }\n        if (fullscreen) {\n            int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;\n            decorView.setSystemUiVisibility(uiOptions);\n            return;\n        }\n        if (actionBar != null) {\n            actionBar.show();\n        }\n        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);\n            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n            window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n            window.setStatusBarColor(defaultStatusBarColor);\n            return;\n        }\n        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n    }\n\n    // public static void mediaScanFile(@NonNull final Context context,\n    //                                  @NonNull File file,\n    //                                  @NonNull final OnScanCompletedListener callback) {\n    //     //noinspection UnstableApiUsage\n    //     final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(Files.getFileExtension(file.getName()));\n    //     MediaScannerConnection.scanFile(\n    //             context,\n    //             new String[]{file.getAbsolutePath()},\n    //             new String[]{mimeType},\n    //             callback\n    //     );\n    // }\n\n    public static void showKeyboard(@NonNull final View view) {\n        try {\n            final Context context = view.getContext();\n            if (context == null) return;\n            final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);\n            if (imm == null) return;\n            view.requestFocus();\n            final boolean shown = imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);\n            if (!shown) {\n                Log.e(TAG, \"showKeyboard: System did not display the keyboard\");\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"showKeyboard: \", e);\n        }\n    }\n\n    public static void hideKeyboard(final View view) {\n        if (view == null) return;\n        final Context context = view.getContext();\n        if (context == null) return;\n        try {\n            final InputMethodManager manager = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);\n            if (manager == null) return;\n            manager.hideSoftInputFromWindow(view.getWindowToken(), 0);\n        } catch (Exception e) {\n            Log.e(TAG, \"hideKeyboard: \", e);\n        }\n    }\n\n    public static Drawable getAnimatableDrawable(@NonNull final Context context,\n                                                 @DrawableRes final int drawableResId) {\n        final Drawable drawable;\n        if (Build.VERSION.SDK_INT >= 24) {\n            drawable = ContextCompat.getDrawable(context, drawableResId);\n        } else {\n            drawable = AnimatedVectorDrawableCompat.create(context, drawableResId);\n        }\n        return drawable;\n    }\n\n    public static void enabledKeepScreenOn(@NonNull final Activity activity) {\n        final Window window = activity.getWindow();\n        if (window == null) return;\n        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n    }\n\n    public static void disableKeepScreenOn(@NonNull final Activity activity) {\n        final Window window = activity.getWindow();\n        if (window == null) return;\n        window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n    }\n\n    public static <T> void moveItem(int sourceIndex, int targetIndex, List<T> list) {\n        if (sourceIndex <= targetIndex) {\n            Collections.rotate(list.subList(sourceIndex, targetIndex + 1), -1);\n        } else {\n            Collections.rotate(list.subList(targetIndex, sourceIndex + 1), 1);\n        }\n    }\n\n    // public static void scanDocumentFile(@NonNull final Context context,\n    //                                     @NonNull final DocumentFile documentFile,\n    //                                     @NonNull final OnScanCompletedListener callback) {\n    //     if (!documentFile.isFile() || !documentFile.exists()) {\n    //         Log.d(TAG, \"scanDocumentFile: \" + documentFile);\n    //         callback.onScanCompleted(null, null);\n    //         return;\n    //     }\n    //     File file = null;\n    //     try {\n    //         file = getDocumentFileRealPath(context, documentFile);\n    //     } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {\n    //         Log.e(TAG, \"scanDocumentFile: \", e);\n    //     }\n    //     if (file == null) return;\n    //     MediaScannerConnection.scanFile(context,\n    //                                     new String[]{file.getAbsolutePath()},\n    //                                     new String[]{documentFile.getType()},\n    //                                     callback);\n    // }\n\n    public static File getDocumentFileRealPath(@NonNull final Context context,\n                                               @NonNull final DocumentFile documentFile)\n            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {\n        final String docId = DocumentsContract.getDocumentId(documentFile.getUri());\n        final String[] split = docId.split(\":\");\n        final String type = split[0];\n\n        if (type.equalsIgnoreCase(\"primary\")) {\n            return new File(Environment.getExternalStorageDirectory(), split[1]);\n        } else if (type.equalsIgnoreCase(\"raw\")) {\n            return new File(split[1]);\n        } else {\n            if (volumes == null) {\n                final StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);\n                if (sm == null) return null;\n                final Method getVolumeListMethod = sm.getClass().getMethod(\"getVolumeList\");\n                volumes = (Object[]) getVolumeListMethod.invoke(sm);\n            }\n            if (volumes == null) return null;\n            for (Object volume : volumes) {\n                final Method getUuidMethod = volume.getClass().getMethod(\"getUuid\");\n                final String uuid = (String) getUuidMethod.invoke(volume);\n\n                if (uuid != null && uuid.equalsIgnoreCase(type)) {\n                    final Method getPathMethod = volume.getClass().getMethod(\"getPath\");\n                    final String path = (String) getPathMethod.invoke(volume);\n                    return new File(path, split[1]);\n                }\n            }\n        }\n\n        return null;\n    }\n\n    public static void setupSelectedDir(@NonNull final Context context,\n                                        @NonNull final Intent intent) throws DownloadUtils.ReselectDocumentTreeException {\n        final Uri dirUri = intent.getData();\n        Log.d(TAG, \"onActivityResult: \" + dirUri);\n        if (dirUri == null) return;\n        final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n        context.getContentResolver().takePersistableUriPermission(dirUri, takeFlags);\n        // re-init DownloadUtils\n        DownloadUtils.init(context, dirUri.toString());\n    }\n\n    @NonNull\n    public static Point getNavigationBarSize(@NonNull Context context) {\n        Point appUsableSize = getAppUsableScreenSize(context);\n        Point realScreenSize = getRealScreenSize(context);\n\n        // navigation bar on the right\n        if (appUsableSize.x < realScreenSize.x) {\n            return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y);\n        }\n\n        // navigation bar at the bottom\n        if (appUsableSize.y < realScreenSize.y) {\n            return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y);\n        }\n\n        // navigation bar is not present\n        return new Point();\n    }\n\n    @NonNull\n    public static Point getAppUsableScreenSize(@NonNull Context context) {\n        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Display display = windowManager.getDefaultDisplay();\n        Point size = new Point();\n        display.getSize(size);\n        return size;\n    }\n\n    @NonNull\n    public static Point getRealScreenSize(@NonNull Context context) {\n        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Display display = windowManager.getDefaultDisplay();\n        Point size = new Point();\n        display.getRealSize(size);\n        return size;\n    }\n\n    public static <F, S> LiveData<Pair<F, S>> zipLiveData(@NonNull final LiveData<F> firstLiveData,\n                                                          @NonNull final LiveData<S> secondLiveData) {\n        final ZippedLiveData<F, S> zippedLiveData = new ZippedLiveData<>();\n        zippedLiveData.addFirstSource(firstLiveData);\n        zippedLiveData.addSecondSource(secondLiveData);\n        return zippedLiveData;\n    }\n\n    public static class ZippedLiveData<F, S> extends MediatorLiveData<Pair<F, S>> {\n        private F lastF;\n        private S lastS;\n\n        private void update() {\n            F localLastF = lastF;\n            S localLastS = lastS;\n            if (localLastF != null && localLastS != null) {\n                setValue(new Pair<>(localLastF, localLastS));\n            }\n        }\n\n        public void addFirstSource(@NonNull final LiveData<F> firstLiveData) {\n            addSource(firstLiveData, f -> {\n                lastF = f;\n                update();\n            });\n        }\n\n        public void addSecondSource(@NonNull final LiveData<S> secondLiveData) {\n            addSource(secondLiveData, s -> {\n                lastS = s;\n                update();\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/ViewUtils.kt",
    "content": "@file:JvmName(\"ViewUtils\")\n\npackage awais.instagrabber.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.GradientDrawable\nimport android.graphics.drawable.ShapeDrawable\nimport android.graphics.drawable.shapes.RoundRectShape\nimport android.os.Build\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport android.widget.TextView\nimport androidx.annotation.ColorInt\nimport androidx.core.content.res.ResourcesCompat\nimport androidx.core.util.Pair\nimport androidx.dynamicanimation.animation.FloatPropertyCompat\nimport androidx.dynamicanimation.animation.SpringAnimation\nimport kotlin.jvm.internal.Intrinsics\n\nfun createRoundRectDrawableWithIcon(context: Context, rad: Int, iconRes: Int): Drawable? {\n    val defaultDrawable = ShapeDrawable(RoundRectShape(FloatArray(8) { rad.toFloat() }, null, null))\n    defaultDrawable.paint.color = -0x1\n    val d = ResourcesCompat.getDrawable(context.resources, iconRes, null) ?: return null\n    val drawable = d.mutate()\n    return CombinedDrawable(defaultDrawable, drawable)\n}\n\nfun createRoundRectDrawable(rad: Int, defaultColor: Int): Drawable {\n    val defaultDrawable = ShapeDrawable(RoundRectShape(FloatArray(8) { rad.toFloat() }, null, null))\n    defaultDrawable.paint.color = defaultColor\n    return defaultDrawable\n}\n\nfun createFrame(\n    width: Int,\n    height: Float,\n    gravity: Int,\n    leftMargin: Float,\n    topMargin: Float,\n    rightMargin: Float,\n    bottomMargin: Float\n): FrameLayout.LayoutParams {\n    val layoutParams = FrameLayout.LayoutParams(getSize(width.toFloat()), getSize(height), gravity)\n    layoutParams.setMargins(\n        Utils.convertDpToPx(leftMargin), Utils.convertDpToPx(topMargin), Utils.convertDpToPx(rightMargin),\n        Utils.convertDpToPx(bottomMargin)\n    )\n    return layoutParams\n}\n\nfun createGradientDrawable(\n    orientation: GradientDrawable.Orientation?,\n    @ColorInt colors: IntArray?\n): GradientDrawable {\n    val drawable = GradientDrawable(orientation, colors)\n    drawable.shape = GradientDrawable.RECTANGLE\n    return drawable\n}\n\nprivate fun getSize(size: Float): Int {\n    return if (size < 0) size.toInt() else Utils.convertDpToPx(size)\n}\n\nfun measure(view: View, parent: View): Pair<Int, Int> {\n    view.measure(\n        View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.UNSPECIFIED),\n        View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)\n    )\n    return Pair(view.measuredHeight, view.measuredWidth)\n}\n\nfun getTextViewValueWidth(textView: TextView, text: String?): Float {\n    return textView.paint.measureText(text)\n}\n\n/**\n * Creates [SpringAnimation] for object.\n * If finalPosition is not [Float.NaN] then create [SpringAnimation] with\n * [SpringForce.mFinalPosition].\n *\n * @param object        Object\n * @param property      object's property to be animated.\n * @param finalPosition [SpringForce.mFinalPosition] Final position of spring.\n * @return [SpringAnimation]\n */\nfun springAnimationOf(\n    `object`: Any?,\n    property: FloatPropertyCompat<Any?>?,\n    finalPosition: Float?\n): SpringAnimation {\n    return finalPosition?.let { SpringAnimation(`object`, property, it) } ?: SpringAnimation(`object`, property)\n}\n\nfun suppressLayoutCompat(`$this$suppressLayoutCompat`: ViewGroup, suppress: Boolean) {\n    Intrinsics.checkNotNullParameter(`$this$suppressLayoutCompat`, \"\\$this\\$suppressLayoutCompat\")\n    if (Build.VERSION.SDK_INT >= 29) {\n        `$this$suppressLayoutCompat`.suppressLayout(suppress)\n    } else {\n        hiddenSuppressLayout(`$this$suppressLayoutCompat`, suppress)\n    }\n}\n\nprivate var tryHiddenSuppressLayout = true\n\n@SuppressLint(\"NewApi\")\nprivate fun hiddenSuppressLayout(group: ViewGroup, suppress: Boolean) {\n    if (tryHiddenSuppressLayout) {\n        try {\n            group.suppressLayout(suppress)\n        } catch (var3: NoSuchMethodError) {\n            tryHiddenSuppressLayout = false\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/VoiceRecorder.java",
    "content": "package awais.instagrabber.utils;\n\nimport android.app.Application;\nimport android.content.ContentResolver;\nimport android.media.MediaRecorder;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.os.ParcelFileDescriptor;\nimport android.util.Log;\nimport android.webkit.MimeTypeMap;\n\nimport androidx.annotation.NonNull;\nimport androidx.documentfile.provider.DocumentFile;\n\nimport java.io.IOException;\nimport java.io.File;\nimport java.time.format.DateTimeFormatter;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class VoiceRecorder {\n    private static final String TAG = VoiceRecorder.class.getSimpleName();\n    private static final String FILE_PREFIX = \"recording\";\n    private static final String EXTENSION = \"mp4\";\n    private static final String MIME_TYPE = MimeTypeMap.getSingleton().getMimeTypeFromExtension(EXTENSION);\n    private static final int AUDIO_SAMPLE_RATE = 44100;\n    private static final int AUDIO_BIT_DEPTH = 16;\n    private static final int AUDIO_BIT_RATE = AUDIO_SAMPLE_RATE * AUDIO_BIT_DEPTH;\n    private static final String FILE_FORMAT = \"yyyy-MM-dd-HH-mm-ss-SSS\";\n    private static final DateTimeFormatter SIMPLE_DATE_FORMAT = DateTimeFormatter.ofPattern(FILE_FORMAT, Locale.US);\n\n    private final List<Float> waveform = new ArrayList<>();\n    private final DocumentFile recordingsDir;\n    private final VoiceRecorderCallback callback;\n\n    private MediaRecorder recorder;\n    private DocumentFile audioTempFile;\n    private MaxAmpHandler maxAmpHandler;\n    private boolean stopped;\n\n    public VoiceRecorder(@NonNull final DocumentFile recordingsDir, final VoiceRecorderCallback callback) {\n        this.recordingsDir = recordingsDir;\n        this.callback = callback;\n    }\n\n    public void startRecording(final ContentResolver contentResolver) {\n        stopped = false;\n        ParcelFileDescriptor parcelFileDescriptor = null;\n        try {\n            recorder = new MediaRecorder();\n            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);\n            recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);\n            deleteTempAudioFile();\n            audioTempFile = getAudioRecordFile();\n            parcelFileDescriptor = contentResolver.openFileDescriptor(audioTempFile.getUri(), \"rwt\");\n            recorder.setOutputFile(parcelFileDescriptor.getFileDescriptor());\n            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);\n            recorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);\n            recorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);\n            recorder.prepare();\n            waveform.clear();\n            maxAmpHandler = new MaxAmpHandler(waveform);\n            recorder.start();\n            if (callback != null) {\n                callback.onStart();\n            }\n            getMaxAmp();\n        } catch (Exception e) {\n            Log.e(TAG, \"Audio recording failed\", e);\n            deleteTempAudioFile();\n        } finally {\n            if (parcelFileDescriptor != null) {\n                try {\n                    parcelFileDescriptor.close();\n                } catch (IOException ignored) {}\n            }\n        }\n    }\n\n    public void stopRecording(final boolean cancelled) {\n        stopped = true;\n        if (maxAmpHandler != null) {\n            maxAmpHandler.removeCallbacks(getMaxAmpRunnable);\n        }\n        if (recorder == null) {\n            if (callback != null) {\n                callback.onCancel();\n            }\n            return;\n        }\n        try {\n            recorder.stop();\n            recorder.release();\n            recorder = null;\n            // processWaveForm();\n        } catch (Exception e) {\n            Log.e(TAG, \"stopRecording: error\", e);\n            deleteTempAudioFile();\n        }\n        if (cancelled) {\n            deleteTempAudioFile();\n            if (callback != null) {\n                callback.onCancel();\n            }\n            return;\n        }\n        if (callback != null) {\n            callback.onComplete(new VoiceRecordingResult(MIME_TYPE, audioTempFile, waveform));\n        }\n    }\n\n    private static class MaxAmpHandler extends Handler {\n        private final List<Float> waveform;\n\n        public MaxAmpHandler(final List<Float> waveform) {\n            this.waveform = waveform;\n        }\n\n        @Override\n        public void handleMessage(@NonNull final Message msg) {\n            if (waveform == null) return;\n            waveform.add(msg.obj instanceof Float ? (Float) msg.obj : 0f);\n        }\n    }\n\n    private final Runnable getMaxAmpRunnable = this::getMaxAmp;\n\n    private void getMaxAmp() {\n        if (stopped || recorder == null || maxAmpHandler == null) return;\n        final float value = (float) Math.pow(2.0d, (Math.log10((double) recorder.getMaxAmplitude() / 2700.0d) * 20.0d) / 6.0d);\n        maxAmpHandler.postDelayed(getMaxAmpRunnable, 100);\n        Message msg = Message.obtain();\n        msg.obj = value;\n        maxAmpHandler.sendMessage(msg);\n    }\n\n    // private void processWaveForm() {\n    //     // if (waveform == null || waveform.isEmpty()) return;\n    //     final Optional<Float> maxAmplitudeOptional = waveform.stream().max(Float::compareTo);\n    //     if (!maxAmplitudeOptional.isPresent()) return;\n    //     final float maxAmp = maxAmplitudeOptional.get();\n    //     final List<Float> normalised = waveform.stream()\n    //                                            .map(amp -> amp / maxAmp)\n    //                                            .map(amp -> amp < 0.01f ? 0f : amp)\n    //                                            .collect(Collectors.toList());\n    //     // final List<Float> normalised = waveform.stream()\n    //     //                                        .map(amp -> amp * 1.0f / 32768)\n    //     //                                        .collect(Collectors.toList());\n    //     // Log.d(TAG, \"processWaveForm: \" + waveform);\n    //     Log.d(TAG, \"processWaveForm: \" + normalised);\n    // }\n\n    @NonNull\n    private DocumentFile getAudioRecordFile() {\n        final String name = String.format(\"%s-%s.%s\", FILE_PREFIX, LocalDateTime.now().format(SIMPLE_DATE_FORMAT), EXTENSION);\n        DocumentFile file = recordingsDir.findFile(name);\n        if (file == null || !file.exists()) {\n            file = recordingsDir.createFile(MIME_TYPE, name);\n        }\n        return file;\n    }\n\n    private void deleteTempAudioFile() {\n        if (audioTempFile == null) {\n            //noinspection ResultOfMethodCallIgnored\n            getAudioRecordFile().delete();\n            return;\n        }\n        final boolean deleted = audioTempFile.delete();\n        if (!deleted) {\n            Log.w(TAG, \"stopRecording: file not deleted\");\n        }\n        audioTempFile = null;\n    }\n\n    public static class VoiceRecordingResult {\n        private final String mimeType;\n        private final DocumentFile file;\n        private final List<Float> waveform;\n        private final int samplingFreq = 10;\n\n        public VoiceRecordingResult(final String mimeType, final DocumentFile file, final List<Float> waveform) {\n            this.mimeType = mimeType;\n            this.file = file;\n            this.waveform = waveform;\n        }\n\n        public String getMimeType() {\n            return mimeType;\n        }\n\n        public DocumentFile getFile() {\n            return file;\n        }\n\n        public List<Float> getWaveform() {\n            return waveform;\n        }\n\n        public int getSamplingFreq() {\n            return samplingFreq;\n        }\n    }\n\n    public interface VoiceRecorderCallback {\n        void onStart();\n\n        void onComplete(final VoiceRecordingResult voiceRecordingResult);\n\n        void onCancel();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.kt",
    "content": "package awais.instagrabber.utils.emoji\n\nimport android.util.Log\nimport awais.instagrabber.customviews.emoji.Emoji\nimport awais.instagrabber.customviews.emoji.EmojiCategory\nimport awais.instagrabber.customviews.emoji.EmojiCategoryType\nimport awais.instagrabber.utils.extensions.TAG\nimport com.google.gson.JsonDeserializationContext\nimport com.google.gson.JsonDeserializer\nimport com.google.gson.JsonElement\nimport com.google.gson.JsonParseException\nimport java.lang.reflect.Type\n\nclass EmojiCategoryDeserializer : JsonDeserializer<EmojiCategory> {\n\n    @Throws(JsonParseException::class)\n    override fun deserialize(\n        json: JsonElement,\n        typeOfT: Type,\n        context: JsonDeserializationContext\n    ): EmojiCategory {\n        val jsonObject = json.asJsonObject\n        val typeElement = jsonObject[\"type\"]\n        val emojisObject = jsonObject.getAsJsonObject(\"emojis\")\n        if (typeElement == null || emojisObject == null) {\n            throw JsonParseException(\"Invalid json for EmojiCategory\")\n        }\n        val typeString = typeElement.asString\n        val type: EmojiCategoryType = try {\n            EmojiCategoryType.valueOf(typeString)\n        } catch (e: IllegalArgumentException) {\n            Log.e(TAG, \"deserialize: \", e)\n            EmojiCategoryType.OTHERS\n        }\n        val emojis: MutableMap<String, Emoji> = linkedMapOf()\n        for ((unicode, value) in emojisObject.entrySet()) {\n            if (unicode == null || value == null) {\n                throw JsonParseException(\"Invalid json for EmojiCategory\")\n            }\n            emojis[unicode] = context.deserialize(value, Emoji::class.java)\n        }\n        return EmojiCategory(type, emojis)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.kt",
    "content": "package awais.instagrabber.utils.emoji\n\nimport awais.instagrabber.customviews.emoji.Emoji\nimport com.google.gson.JsonDeserializationContext\nimport com.google.gson.JsonDeserializer\nimport com.google.gson.JsonElement\nimport com.google.gson.JsonParseException\nimport java.lang.reflect.Type\n\nclass EmojiDeserializer : JsonDeserializer<Emoji> {\n    @Throws(JsonParseException::class)\n    override fun deserialize(\n        json: JsonElement,\n        typeOfT: Type,\n        context: JsonDeserializationContext\n    ): Emoji {\n        val jsonObject = json.asJsonObject\n        val unicodeElement = jsonObject[\"unicode\"]\n        val nameElement = jsonObject[\"name\"]\n        if (unicodeElement == null || nameElement == null) {\n            throw JsonParseException(\"Invalid json for Emoji class\")\n        }\n        val variantsElement = jsonObject[\"variants\"]\n        val variants: MutableList<Emoji> = mutableListOf()\n        if (variantsElement != null) {\n            val variantsArray = variantsElement.asJsonArray\n            for (variantElement in variantsArray) {\n                val variant = context.deserialize<Emoji>(variantElement, Emoji::class.java)\n                if (variant != null) {\n                    variants.add(variant)\n                }\n            }\n        }\n        return Emoji(\n            unicodeElement.asString,\n            nameElement.asString,\n            variants\n        )\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.kt",
    "content": "package awais.instagrabber.utils.emoji\n\nimport android.content.Context\nimport android.util.Log\nimport awais.instagrabber.R\nimport awais.instagrabber.customviews.emoji.Emoji\nimport awais.instagrabber.customviews.emoji.EmojiCategory\nimport awais.instagrabber.customviews.emoji.EmojiCategoryType\nimport awais.instagrabber.utils.NetworkUtils\nimport awais.instagrabber.utils.SingletonHolder\nimport awais.instagrabber.utils.extensions.TAG\nimport com.google.gson.FieldNamingPolicy\nimport com.google.gson.GsonBuilder\nimport com.google.gson.reflect.TypeToken\n\nclass EmojiParser private constructor(context: Context) {\n    var allEmojis: Map<String, Emoji> = emptyMap()\n    var categoryMap: Map<EmojiCategoryType, EmojiCategory> = emptyMap()\n    val emojiCategories: List<EmojiCategory> by lazy {\n        categoryMap.values.toList()\n    }\n\n    fun getEmoji(emoji: String): Emoji? {\n        return allEmojis[emoji]\n    }\n\n    init {\n        try {\n            context.applicationContext.resources.openRawResource(R.raw.emojis).use { `in` ->\n                val json = NetworkUtils.readFromInputStream(`in`)\n                val gson = GsonBuilder().apply {\n                    setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)\n                    registerTypeAdapter(EmojiCategory::class.java, EmojiCategoryDeserializer())\n                    registerTypeAdapter(Emoji::class.java, EmojiDeserializer())\n                    setLenient()\n                }.create()\n                val type = object : TypeToken<Map<EmojiCategoryType, EmojiCategory>>() {}.type\n                categoryMap = gson.fromJson(json, type)\n                // Log.d(TAG, \"EmojiParser: \" + categoryMap);\n                allEmojis = categoryMap\n                    .flatMap { (_, emojiCategory) -> emojiCategory.emojis.values }\n                    .flatMap { listOf(it) + it.variants }\n                    .filterNotNull()\n                    .map { it.unicode to it }\n                    .toMap()\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"EmojiParser: \", e)\n        }\n    }\n\n    companion object : SingletonHolder<EmojiParser, Context>(::EmojiParser)\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/extensions/AnyExtensions.kt",
    "content": "package awais.instagrabber.utils.extensions\n\nval Any.TAG: String\n    get() {\n        return if (!javaClass.isAnonymousClass) {\n            val name = javaClass.simpleName\n            if (name.length <= 23) name else name.substring(0, 23) // first 23 chars\n        } else {\n            val name = javaClass.name\n            if (name.length <= 23) name else name.substring(name.length - 23, name.length) // last 23 chars\n        }\n    }"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/extensions/StringExtensions.kt",
    "content": "package awais.instagrabber.utils.extensions\n\nfun String.trimAll() = this.trim { it <= ' ' }"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/utils/extensions/UserExtensions.kt",
    "content": "package awais.instagrabber.utils.extensions\n\nimport awais.instagrabber.repositories.responses.User\n\nfun User.isReallyPrivate(currentUser: User? = null): Boolean {\n    if (currentUser == null) return this.isPrivate\n    if (this.pk == currentUser.pk) return false\n    return this.friendshipStatus?.following == false && this.isPrivate\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.app.Application;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.lifecycle.AndroidViewModel;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\n\nimport awais.instagrabber.db.repositories.AccountRepository;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.webservices.UserRepository;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class AppStateViewModel extends AndroidViewModel {\n    private static final String TAG = AppStateViewModel.class.getSimpleName();\n\n    private final String cookie;\n    private final MutableLiveData<Resource<User>> currentUser = new MutableLiveData<>(Resource.loading(null));\n\n    private AccountRepository accountRepository;\n\n    private UserRepository userRepository;\n\n    public AppStateViewModel(@NonNull final Application application) {\n        super(application);\n        // Log.d(TAG, \"AppStateViewModel: constructor\");\n        cookie = settingsHelper.getString(Constants.COOKIE);\n        final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;\n        if (!isLoggedIn) {\n            currentUser.postValue(Resource.success(null));\n            return;\n        }\n        userRepository = UserRepository.Companion.getInstance();\n        accountRepository = AccountRepository.Companion.getInstance(application);\n        fetchProfileDetails();\n    }\n\n    @Nullable\n    public Resource<User> getCurrentUser() {\n        return currentUser.getValue();\n    }\n\n    public LiveData<Resource<User>> getCurrentUserLiveData() {\n        return currentUser;\n    }\n\n    public void fetchProfileDetails() {\n        currentUser.postValue(Resource.loading(null));\n        final long uid = CookieUtils.getUserIdFromCookie(cookie);\n        if (uid == 0L) {\n            currentUser.postValue(Resource.success(null));\n            return;\n        }\n        userRepository.getUserInfo(uid, CoroutineUtilsKt.getContinuation((user, throwable) -> {\n            if (throwable != null) {\n                Log.e(TAG, \"onFailure: \", throwable);\n                final Resource<User> userResource = currentUser.getValue();\n                final User backup = userResource != null && userResource.data != null ? userResource.data : new User(uid);\n                currentUser.postValue(Resource.error(throwable.getMessage(), backup));\n                return;\n            }\n            currentUser.postValue(Resource.success(user));\n            if (accountRepository != null && user != null) {\n                accountRepository.insertOrUpdateAccount(\n                        user.getPk(),\n                        user.getUsername(),\n                        cookie,\n                        user.getFullName() != null ? user.getFullName() : \"\",\n                        user.getProfilePicUrl(),\n                        CoroutineUtilsKt.getContinuation((account, throwable1) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {\n                            if (throwable1 != null) {\n                                Log.e(TAG, \"updateAccountInfo: \", throwable1);\n                            }\n                        }), Dispatchers.getIO())\n                );\n            }\n        }, Dispatchers.getIO()));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/ArchivesViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic class ArchivesViewModel extends ViewModel {\n    private MutableLiveData<List<Story>> list;\n\n    public MutableLiveData<List<Story>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport com.google.common.collect.ImmutableList;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.OptionalInt;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.repositories.responses.ChildCommentsFetchResponse;\nimport awais.instagrabber.repositories.responses.CommentsFetchResponse;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.Utils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.webservices.CommentService;\nimport awais.instagrabber.webservices.GraphQLRepository;\nimport awais.instagrabber.webservices.ServiceCallback;\nimport kotlin.coroutines.Continuation;\nimport kotlinx.coroutines.Dispatchers;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class CommentsViewerViewModel extends ViewModel {\n    private static final String TAG = CommentsViewerViewModel.class.getSimpleName();\n\n    private final MutableLiveData<Long> currentUserId = new MutableLiveData<>(0L);\n    private final MutableLiveData<Resource<List<Comment>>> rootList = new MutableLiveData<>();\n    private final MutableLiveData<Integer> rootCount = new MutableLiveData<>(0);\n    private final MutableLiveData<Resource<List<Comment>>> replyList = new MutableLiveData<>();\n    private final GraphQLRepository graphQLRepository;\n\n    private String shortCode;\n    private String postId;\n    private String rootCursor;\n    private boolean rootHasNext = true;\n    private Comment repliesParent, replyTo;\n    private String repliesCursor;\n    private boolean repliesHasNext = true;\n    private final CommentService commentService;\n    private List<Comment> prevReplies;\n    private String prevRepliesCursor;\n    private boolean prevRepliesHasNext = true;\n\n    private final ServiceCallback<CommentsFetchResponse> ccb = new ServiceCallback<CommentsFetchResponse>() {\n        @Override\n        public void onSuccess(final CommentsFetchResponse result) {\n            // Log.d(TAG, \"onSuccess: \" + result);\n            if (result == null) {\n                rootList.postValue(Resource.error(R.string.generic_null_response, getPrevList(rootList)));\n                return;\n            }\n            List<Comment> comments = result.getComments();\n            if (rootCursor == null) {\n                rootCount.postValue(result.getCommentCount());\n            }\n            if (rootCursor != null) {\n                comments = mergeList(rootList, comments);\n            }\n            rootCursor = result.getNextMinId();\n            rootHasNext = !TextUtils.isEmpty(rootCursor);\n            rootList.postValue(Resource.success(comments));\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            Log.e(TAG, \"onFailure: \", t);\n            rootList.postValue(Resource.error(t.getMessage(), getPrevList(rootList)));\n        }\n    };\n    private final ServiceCallback<ChildCommentsFetchResponse> rcb = new ServiceCallback<ChildCommentsFetchResponse>() {\n        @Override\n        public void onSuccess(final ChildCommentsFetchResponse result) {\n            // Log.d(TAG, \"onSuccess: \" + result);\n            if (result == null) {\n                rootList.postValue(Resource.error(R.string.generic_null_response, getPrevList(replyList)));\n                return;\n            }\n            List<Comment> comments = result.getChildComments();\n            // Replies\n            if (repliesCursor == null) {\n                // add parent to top of replies\n                comments = ImmutableList.<Comment>builder()\n                        .add(repliesParent)\n                        .addAll(comments)\n                        .build();\n            }\n            if (repliesCursor != null) {\n                comments = mergeList(replyList, comments);\n            }\n            repliesCursor = result.getNextMaxChildCursor();\n            repliesHasNext = result.getHasMoreTailChildComments();\n            replyList.postValue(Resource.success(comments));\n        }\n\n        @Override\n        public void onFailure(final Throwable t) {\n            Log.e(TAG, \"onFailure: \", t);\n            replyList.postValue(Resource.error(t.getMessage(), getPrevList(replyList)));\n        }\n    };\n\n    public CommentsViewerViewModel() {\n        graphQLRepository = GraphQLRepository.Companion.getInstance();\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);\n        final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);\n        commentService = CommentService.getInstance(deviceUuid, csrfToken, userIdFromCookie);\n    }\n\n    public void setCurrentUser(final User currentUser) {\n        currentUserId.postValue(currentUser == null ? 0 : currentUser.getPk());\n    }\n\n    public void setPostDetails(final String shortCode, final String postId, final long postUserId) {\n        this.shortCode = shortCode;\n        this.postId = postId;\n    }\n\n    public LiveData<Long> getCurrentUserId() {\n        return currentUserId;\n    }\n\n    @Nullable\n    public Comment getRepliesParent() {\n        return repliesParent;\n    }\n\n    @Nullable\n    public void setReplyTo(final Comment replyTo) {\n        this.replyTo = replyTo;\n    }\n\n    public LiveData<Resource<List<Comment>>> getRootList() {\n        return rootList;\n    }\n\n    public LiveData<Resource<List<Comment>>> getReplyList() {\n        return replyList;\n    }\n\n    public LiveData<Integer> getRootCommentsCount() {\n        return rootCount;\n    }\n\n    public void fetchComments() {\n        if (shortCode == null || postId == null) return;\n        if (!rootHasNext) return;\n        rootList.postValue(Resource.loading(getPrevList(rootList)));\n        if (currentUserId.getValue() != 0L) {\n            commentService.fetchComments(postId, rootCursor, ccb);\n            return;\n        }\n        graphQLRepository.fetchComments(\n                shortCode,\n                true,\n                rootCursor,\n                enqueueRequest(true, shortCode, ccb)\n        );\n    }\n\n    public void fetchReplies() {\n        if (repliesParent == null) return;\n        fetchReplies(repliesParent.getPk());\n    }\n\n    public void fetchReplies(@NonNull final String commentId) {\n        if (!repliesHasNext) return;\n        final List<Comment> list;\n        if (repliesParent != null && !Objects.equals(repliesParent.getPk(), commentId)) {\n            repliesCursor = null;\n            repliesHasNext = false;\n            list = Collections.emptyList();\n        } else {\n            list = getPrevList(replyList);\n        }\n        replyList.postValue(Resource.loading(list));\n        if (currentUserId.getValue() != 0L) {\n            commentService.fetchChildComments(postId, commentId, repliesCursor, rcb);\n            return;\n        }\n        graphQLRepository.fetchComments(commentId, false, repliesCursor, enqueueRequest(false, commentId, rcb));\n    }\n\n    private Continuation<String> enqueueRequest(final boolean root,\n                                                final String shortCodeOrCommentId,\n                                                @SuppressWarnings(\"rawtypes\") final ServiceCallback callback) {\n        return CoroutineUtilsKt.getContinuation((response, throwable) -> {\n            if (throwable != null) {\n                callback.onFailure(throwable);\n                return;\n            }\n            if (response == null) {\n                Log.e(TAG, \"Error occurred while fetching gql comments of \" + shortCodeOrCommentId);\n                //noinspection unchecked\n                callback.onSuccess(null);\n                return;\n            }\n            try {\n                final JSONObject body = root ? new JSONObject(response).getJSONObject(\"data\")\n                                                                       .getJSONObject(\"shortcode_media\")\n                                                                       .getJSONObject(\"edge_media_to_parent_comment\")\n                                             : new JSONObject(response).getJSONObject(\"data\")\n                                                                       .getJSONObject(\"comment\")\n                                                                       .getJSONObject(\"edge_threaded_comments\");\n                final int count = body.optInt(\"count\");\n                final JSONObject pageInfo = body.getJSONObject(\"page_info\");\n                final boolean hasNextPage = pageInfo.getBoolean(\"has_next_page\");\n                final String endCursor = pageInfo.isNull(\"end_cursor\") || !hasNextPage ? null : pageInfo.optString(\"end_cursor\");\n                final JSONArray commentsJsonArray = body.getJSONArray(\"edges\");\n                final ImmutableList.Builder<Comment> builder = ImmutableList.builder();\n                for (int i = 0; i < commentsJsonArray.length(); i++) {\n                    final Comment commentModel = getComment(commentsJsonArray.getJSONObject(i).getJSONObject(\"node\"), root);\n                    builder.add(commentModel);\n                }\n                final Object result = root ? new CommentsFetchResponse(count, endCursor, builder.build())\n                                           : new ChildCommentsFetchResponse(count, endCursor, builder.build(), hasNextPage);\n                //noinspection unchecked\n                callback.onSuccess(result);\n            } catch (Exception e) {\n                Log.e(TAG, \"onResponse\", e);\n                callback.onFailure(e);\n            }\n        }, Dispatchers.getIO());\n    }\n\n    @NonNull\n    private Comment getComment(@NonNull final JSONObject commentJsonObject, final boolean root) throws JSONException {\n        final JSONObject owner = commentJsonObject.getJSONObject(\"owner\");\n        final User user = new User(\n                owner.optLong(Constants.EXTRAS_ID, 0),\n                owner.getString(Constants.EXTRAS_USERNAME),\n                null,\n                false,\n                owner.getString(\"profile_pic_url\"),\n                owner.optBoolean(\"is_verified\"));\n        final JSONObject likedBy = commentJsonObject.optJSONObject(\"edge_liked_by\");\n        final String commentId = commentJsonObject.getString(\"id\");\n        final JSONObject childCommentsJsonObject = commentJsonObject.optJSONObject(\"edge_threaded_comments\");\n        int replyCount = 0;\n        if (childCommentsJsonObject != null) {\n            replyCount = childCommentsJsonObject.optInt(\"count\");\n        }\n        return new Comment(commentId,\n                           commentJsonObject.getString(\"text\"),\n                           commentJsonObject.getLong(\"created_at\"),\n                           likedBy != null ? likedBy.optLong(\"count\", 0) : 0,\n                           commentJsonObject.getBoolean(\"viewer_has_liked\"),\n                           user,\n                           replyCount);\n    }\n\n    @NonNull\n    private List<Comment> getPrevList(@NonNull final LiveData<Resource<List<Comment>>> list) {\n        if (list.getValue() == null) return Collections.emptyList();\n        final Resource<List<Comment>> listResource = list.getValue();\n        if (listResource.data == null) return Collections.emptyList();\n        return listResource.data;\n    }\n\n    private List<Comment> mergeList(@NonNull final LiveData<Resource<List<Comment>>> list,\n                                    final List<Comment> comments) {\n        final List<Comment> prevList = getPrevList(list);\n        if (comments == null) {\n            return prevList;\n        }\n        return ImmutableList.<Comment>builder()\n                .addAll(prevList)\n                .addAll(comments)\n                .build();\n    }\n\n    public void showReplies(final Comment comment) {\n        if (comment == null) return;\n        if (repliesParent == null || !Objects.equals(repliesParent.getPk(), comment.getPk())) {\n            repliesParent = comment;\n            replyTo = comment;\n            prevReplies = null;\n            prevRepliesCursor = null;\n            prevRepliesHasNext = true;\n            fetchReplies(comment.getPk());\n            return;\n        }\n        if (prevReplies != null && !prevReplies.isEmpty()) {\n            // user clicked same comment, show prev loaded replies\n            repliesCursor = prevRepliesCursor;\n            repliesHasNext = prevRepliesHasNext;\n            replyList.postValue(Resource.success(prevReplies));\n            return;\n        }\n        // prev list was null or empty, fetch\n        prevRepliesCursor = null;\n        prevRepliesHasNext = true;\n        fetchReplies(comment.getPk());\n    }\n\n    public LiveData<Resource<Object>> likeComment(@NonNull final Comment comment, final boolean liked, final boolean isReply) {\n        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));\n        final ServiceCallback<Boolean> callback = new ServiceCallback<Boolean>() {\n            @Override\n            public void onSuccess(final Boolean result) {\n                if (result == null || !result) {\n                    data.postValue(Resource.error(R.string.downloader_unknown_error, null));\n                    return;\n                }\n                data.postValue(Resource.success(new Object()));\n                setLiked(isReply, comment, liked);\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                Log.e(TAG, \"Error liking comment\", t);\n                data.postValue(Resource.error(t.getMessage(), null));\n            }\n        };\n        if (liked) {\n            commentService.commentLike(comment.getPk(), callback);\n        } else {\n            commentService.commentUnlike(comment.getPk(), callback);\n        }\n        return data;\n    }\n\n    private void setLiked(final boolean isReply,\n                          @NonNull final Comment comment,\n                          final boolean liked) {\n        final List<Comment> list = getPrevList(isReply ? replyList : rootList);\n        if (list == null) return;\n        final List<Comment> copy = new ArrayList<>(list);\n        OptionalInt indexOpt = IntStream.range(0, copy.size())\n                                        .filter(i -> copy.get(i) != null && Objects.equals(copy.get(i).getPk(), comment.getPk()))\n                                        .findFirst();\n        if (!indexOpt.isPresent()) return;\n        try {\n            final Comment clone = (Comment) comment.clone();\n            clone.setLiked(liked);\n            copy.set(indexOpt.getAsInt(), clone);\n            final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;\n            liveData.postValue(Resource.success(copy));\n        } catch (Exception e) {\n            Log.e(TAG, \"setLiked: \", e);\n        }\n    }\n\n    public LiveData<Resource<Object>> comment(@NonNull final String text,\n                                              final boolean isReply) {\n        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));\n        String replyToId = null;\n        if (isReply && replyTo != null) {\n            replyToId = replyTo.getPk();\n        }\n        if (isReply && replyToId == null) {\n            data.postValue(Resource.error(null, null));\n            return data;\n        }\n        commentService.comment(postId, text, replyToId, new ServiceCallback<Comment>() {\n            @Override\n            public void onSuccess(final Comment result) {\n                if (result == null) {\n                    data.postValue(Resource.error(R.string.downloader_unknown_error, null));\n                    return;\n                }\n                addComment(result, isReply);\n                data.postValue(Resource.success(new Object()));\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                Log.e(TAG, \"Error during comment\", t);\n                data.postValue(Resource.error(t.getMessage(), null));\n            }\n        });\n        return data;\n    }\n\n    private void addComment(@NonNull final Comment comment, final boolean isReply) {\n        final List<Comment> list = getPrevList(isReply ? replyList : rootList);\n        final ImmutableList.Builder<Comment> builder = ImmutableList.builder();\n        if (isReply) {\n            // replies are added to the bottom of the list to preserve chronological order\n            builder.addAll(list)\n                   .add(comment);\n        } else {\n            builder.add(comment)\n                   .addAll(list);\n        }\n        final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;\n        liveData.postValue(Resource.success(builder.build()));\n    }\n\n    public void translate(@NonNull final Comment comment,\n                          @NonNull final ServiceCallback<String> callback) {\n        commentService.translate(comment.getPk(), callback);\n    }\n\n    public LiveData<Resource<Object>> deleteComment(@NonNull final Comment comment, final boolean isReply) {\n        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(Resource.loading(null));\n        commentService.deleteComment(postId, comment.getPk(), new ServiceCallback<Boolean>() {\n            @Override\n            public void onSuccess(final Boolean result) {\n                if (result == null || !result) {\n                    data.postValue(Resource.error(R.string.downloader_unknown_error, null));\n                    return;\n                }\n                removeComment(comment, isReply);\n                data.postValue(Resource.success(new Object()));\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                Log.e(TAG, \"Error deleting comment\", t);\n                data.postValue(Resource.error(t.getMessage(), null));\n            }\n        });\n        return data;\n    }\n\n    private void removeComment(@NonNull final Comment comment, final boolean isReply) {\n        final List<Comment> list = getPrevList(isReply ? replyList : rootList);\n        final List<Comment> updated = list.stream()\n                                          .filter(Objects::nonNull)\n                                          .filter(c -> !Objects.equals(c.getPk(), comment.getPk()))\n                                          .collect(Collectors.toList());\n        final MutableLiveData<Resource<List<Comment>>> liveData = isReply ? replyList : rootList;\n        liveData.postValue(Resource.success(updated));\n    }\n\n    public void clearReplies() {\n        prevRepliesCursor = repliesCursor;\n        prevRepliesHasNext = repliesHasNext;\n        repliesCursor = null;\n        repliesHasNext = true;\n        // cache prev reply list to save time and data if user clicks same comment again\n        prevReplies = getPrevList(replyList);\n        replyList.postValue(Resource.success(Collections.emptyList()));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.managers.InboxManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectInbox\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\n\nclass DirectInboxViewModel : ViewModel() {\n    private val inboxManager: InboxManager = DirectMessagesManager.inboxManager\n    val inbox: LiveData<Resource<DirectInbox?>> = inboxManager.getInbox()\n    val threads: LiveData<List<DirectThread>> = inboxManager.threads\n    val unseenCount: LiveData<Resource<Int?>> = inboxManager.getUnseenCount()\n    val pendingRequestsTotal: LiveData<Int> = inboxManager.getPendingRequestsTotal()\n    val viewer: User? = inboxManager.viewer\n\n    fun fetchInbox() {\n        inboxManager.fetchInbox(viewModelScope)\n    }\n\n    fun refresh() {\n        inboxManager.refresh(viewModelScope)\n    }\n\n    fun onDestroy() {\n        inboxManager.onDestroy()\n    }\n\n    init {\n        inboxManager.fetchInbox(viewModelScope)\n        inboxManager.fetchUnseenCount(viewModelScope)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.managers.DirectMessagesManager.pendingInboxManager\nimport awais.instagrabber.managers.InboxManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectInbox\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\n\nclass DirectPendingInboxViewModel : ViewModel() {\n    private val inboxManager: InboxManager = pendingInboxManager\n    val threads: LiveData<List<DirectThread>> = inboxManager.threads\n    val inbox: LiveData<Resource<DirectInbox?>> = inboxManager.getInbox()\n    val viewer: User? = inboxManager.viewer\n\n    fun fetchInbox() {\n        inboxManager.fetchInbox(viewModelScope)\n    }\n\n    fun refresh() {\n        inboxManager.refresh(viewModelScope)\n    }\n\n    fun onDestroy() {\n        inboxManager.onDestroy()\n    }\n\n    init {\n        inboxManager.fetchInbox(viewModelScope)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.app.Application\nimport androidx.annotation.StringRes\nimport androidx.core.util.Pair\nimport androidx.lifecycle.AndroidViewModel\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.R\nimport awais.instagrabber.dialogs.MultiOptionDialogFragment.Option\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\nimport awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.getCsrfTokenFromCookie\nimport awais.instagrabber.utils.getUserIdFromCookie\n\nclass DirectSettingsViewModel(\n    application: Application,\n    threadId: String,\n    pending: Boolean,\n    currentUser: User,\n) : AndroidViewModel(application) {\n    private val viewerId: Long\n    private val resources = application.resources\n    private val threadManager = DirectMessagesManager.getThreadManager(threadId, pending, currentUser, application.contentResolver)\n\n    val thread: LiveData<DirectThread?> = threadManager.thread\n\n    // public void setThread(@NonNull final DirectThread thread) {\n    //     this.thread = thread;\n    //     inputMode.postValue(thread.getInputMode());\n    //     List<User> users = thread.getUsers();\n    //     final ImmutableList.Builder<User> builder = ImmutableList.<User>builder().add(currentUser);\n    //     if (users != null) {\n    //         builder.addAll(users);\n    //     }\n    //     users = builder.build();\n    //     this.users.postValue(new Pair<>(users, thread.getLeftUsers()));\n    //     // setTitle(thread.getThreadTitle());\n    //     final List<Long> adminUserIds = thread.getAdminUserIds();\n    //     this.adminUserIds.postValue(adminUserIds);\n    //     viewerIsAdmin = adminUserIds.contains(viewerId);\n    //     muted.postValue(thread.getMuted());\n    //     mentionsMuted.postValue(thread.isMentionsMuted());\n    //     approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers());\n    //     isPending.postValue(thread.isPending());\n    //     if (thread.getInputMode() != 1 && thread.isGroup() && viewerIsAdmin) {\n    //         fetchPendingRequests();\n    //     }\n    // }\n    val inputMode: LiveData<Int> = threadManager.inputMode\n\n    fun isGroup(): LiveData<Boolean> = threadManager.isGroup\n\n    fun getUsers(): LiveData<List<User>> = threadManager.usersWithCurrent\n\n    fun getLeftUsers(): LiveData<List<User>> = threadManager.leftUsers\n\n    fun getUsersAndLeftUsers(): LiveData<Pair<List<User>, List<User>>> = threadManager.usersAndLeftUsers\n\n    fun getTitle(): LiveData<String?> = threadManager.threadTitle\n\n    // public void setTitle(final String title) {\n    //     if (title == null) {\n    //         this.title.postValue(\"\");\n    //         return;\n    //     }\n    //     this.title.postValue(title.trim());\n    // }\n    fun getAdminUserIds(): LiveData<List<Long>> = threadManager.adminUserIds\n\n    fun isMuted(): LiveData<Boolean> = threadManager.isMuted\n\n    fun getApprovalRequiredToJoin(): LiveData<Boolean> = threadManager.isApprovalRequiredToJoin\n\n    fun getPendingRequests(): LiveData<DirectThreadParticipantRequestsResponse?> = threadManager.pendingRequests\n\n    fun isPending(): LiveData<Boolean> = threadManager.isPending\n\n    fun isViewerAdmin(): LiveData<Boolean> = threadManager.isViewerAdmin\n\n    fun updateTitle(newTitle: String): LiveData<Resource<Any?>> = threadManager.updateTitle(newTitle, viewModelScope)\n\n    fun addMembers(users: Set<User>): LiveData<Resource<Any?>> = threadManager.addMembers(users, viewModelScope)\n\n    fun removeMember(user: User): LiveData<Resource<Any?>> = threadManager.removeMember(user, viewModelScope)\n\n    private fun makeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.makeAdmin(user, viewModelScope)\n\n    private fun removeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.removeAdmin(user, viewModelScope)\n\n    fun mute(): LiveData<Resource<Any?>> = threadManager.mute(viewModelScope)\n\n    fun unmute(): LiveData<Resource<Any?>> = threadManager.unmute(viewModelScope)\n\n    fun muteMentions(): LiveData<Resource<Any?>> = threadManager.muteMentions(viewModelScope)\n\n    fun unmuteMentions(): LiveData<Resource<Any?>> = threadManager.unmuteMentions(viewModelScope)\n\n    private fun blockUser(user: User): LiveData<Resource<Any?>> = threadManager.blockUser(user, viewModelScope)\n\n    private fun unblockUser(user: User): LiveData<Resource<Any?>> = threadManager.unblockUser(user, viewModelScope)\n\n    private fun restrictUser(user: User): LiveData<Resource<Any?>> = threadManager.restrictUser(user, viewModelScope)\n\n    private fun unRestrictUser(user: User): LiveData<Resource<Any?>> = threadManager.unRestrictUser(user, viewModelScope)\n\n    fun approveUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.approveUsers(users, viewModelScope)\n\n    fun denyUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.denyUsers(users, viewModelScope)\n\n    fun approvalRequired(): LiveData<Resource<Any?>> = threadManager.approvalRequired(viewModelScope)\n\n    fun approvalNotRequired(): LiveData<Resource<Any?>> = threadManager.approvalNotRequired(viewModelScope)\n\n    fun leave(): LiveData<Resource<Any?>> = threadManager.leave(viewModelScope)\n\n    fun end(): LiveData<Resource<Any?>> = threadManager.end(viewModelScope)\n\n    fun createUserOptions(user: User?): ArrayList<Option<String>> {\n        val options: ArrayList<Option<String>> = ArrayList()\n        if (user == null || isSelf(user) || hasLeft(user)) {\n            return options\n        }\n        val viewerIsAdmin: Boolean? = threadManager.isViewerAdmin.value\n        if (viewerIsAdmin != null && viewerIsAdmin) {\n            options.add(Option(getString(R.string.dms_action_kick), ACTION_KICK))\n            val isAdmin: Boolean = threadManager.isAdmin(user)\n            options.add(Option(\n                if (isAdmin) getString(R.string.dms_action_remove_admin) else getString(R.string.dms_action_make_admin),\n                if (isAdmin) ACTION_REMOVE_ADMIN else ACTION_MAKE_ADMIN\n            ))\n        }\n        val blocking: Boolean = user.friendshipStatus?.blocking ?: false\n        options.add(Option(\n            if (blocking) getString(R.string.unblock) else getString(R.string.block),\n            if (blocking) ACTION_UNBLOCK else ACTION_BLOCK\n        ))\n\n        // options.add(new Option<>(getString(R.string.report), ACTION_REPORT));\n        val isGroup: Boolean? = threadManager.isGroup.value\n        if (isGroup != null && isGroup) {\n            val restricted: Boolean = user.friendshipStatus?.isRestricted ?: false\n            options.add(Option(\n                if (restricted) getString(R.string.unrestrict) else getString(R.string.restrict),\n                if (restricted) ACTION_UNRESTRICT else ACTION_RESTRICT\n            ))\n        }\n        return options\n    }\n\n    private fun hasLeft(user: User): Boolean {\n        val leftUsers: List<User> = getLeftUsers().value ?: return false\n        return leftUsers.contains(user)\n    }\n\n    private fun isSelf(user: User): Boolean = user.pk == viewerId\n\n    private fun getString(@StringRes resId: Int): String {\n        return resources.getString(resId)\n    }\n\n    fun doAction(user: User?, action: String?): LiveData<Resource<Any?>>? {\n        return if (user == null || action == null) null else when (action) {\n            ACTION_KICK -> removeMember(user)\n            ACTION_MAKE_ADMIN -> makeAdmin(user)\n            ACTION_REMOVE_ADMIN -> removeAdmin(user)\n            ACTION_BLOCK -> blockUser(user)\n            ACTION_UNBLOCK -> unblockUser(user)\n            ACTION_RESTRICT -> restrictUser(user)\n            ACTION_UNRESTRICT -> unRestrictUser(user)\n            else -> null\n        }\n    }\n\n    fun getInviter(): LiveData<User?> = threadManager.inviter\n\n    companion object {\n        private const val ACTION_KICK = \"kick\"\n        private const val ACTION_MAKE_ADMIN = \"make_admin\"\n        private const val ACTION_REMOVE_ADMIN = \"remove_admin\"\n        private const val ACTION_BLOCK = \"block\"\n        private const val ACTION_UNBLOCK = \"unblock\"\n\n        // private static final String ACTION_REPORT = \"report\";\n        private const val ACTION_RESTRICT = \"restrict\"\n        private const val ACTION_UNRESTRICT = \"unrestrict\"\n    }\n\n    init {\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        viewerId = getUserIdFromCookie(cookie)\n        val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        val csrfToken = getCsrfTokenFromCookie(cookie)\n        require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { \"User is not logged in!\" }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.app.Application\nimport android.content.ContentResolver\nimport android.net.Uri\nimport androidx.documentfile.provider.DocumentFile\nimport androidx.lifecycle.*\nimport awais.instagrabber.customviews.emoji.Emoji\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.managers.DirectMessagesManager.inboxManager\nimport awais.instagrabber.managers.ThreadManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.DirectItem\nimport awais.instagrabber.repositories.responses.directmessages.DirectThread\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.MediaUtils.OnInfoLoadListener\nimport awais.instagrabber.utils.MediaUtils.VideoInfo\nimport awais.instagrabber.utils.VoiceRecorder.VoiceRecorderCallback\nimport awais.instagrabber.utils.VoiceRecorder.VoiceRecordingResult\n\n\nclass DirectThreadViewModel(\n    application: Application,\n    val threadId: String,\n    pending: Boolean,\n    val currentUser: User,\n) : AndroidViewModel(application) {\n    // private val TAG = DirectThreadViewModel::class.java.simpleName\n\n    // private static final String ERROR_INVALID_THREAD = \"Invalid thread\";\n    private val contentResolver: ContentResolver = application.contentResolver\n    private val recordingsDir: DocumentFile? = DownloadUtils.recordingsDir\n    private var voiceRecorder: VoiceRecorder? = null\n    private lateinit var threadManager: ThreadManager\n\n    val viewerId: Long\n    val threadTitle: LiveData<String?> by lazy { threadManager.threadTitle }\n    val thread: LiveData<DirectThread?> by lazy { threadManager.thread }\n    val items: LiveData<List<DirectItem>> by lazy {\n        Transformations.map(threadManager.items) { it.filter { thread -> thread.hideInThread == 0 } }\n    }\n    val isFetching: LiveData<Resource<Any?>> by lazy { threadManager.fetching }\n    val users: LiveData<List<User>> by lazy { threadManager.users }\n    val leftUsers: LiveData<List<User>> by lazy { threadManager.leftUsers }\n    val pendingRequestsCount: LiveData<Int> by lazy { threadManager.pendingRequestsCount }\n    val inputMode: LiveData<Int> by lazy { threadManager.inputMode }\n    val isPending: LiveData<Boolean> by lazy { threadManager.isPending }\n    val replyToItem: LiveData<DirectItem?> by lazy { threadManager.replyToItem }\n\n    fun moveFromPending() {\n        val messagesManager = DirectMessagesManager\n        messagesManager.moveThreadFromPending(threadId)\n        threadManager = messagesManager.getThreadManager(threadId, false, currentUser, contentResolver)\n    }\n\n    fun removeThread() {\n        threadManager.removeThread()\n    }\n\n    fun fetchChats() {\n        threadManager.fetchChats(viewModelScope)\n    }\n\n    fun refreshChats() {\n        threadManager.refreshChats(viewModelScope)\n    }\n\n    fun sendText(text: String): LiveData<Resource<Any?>> {\n        return threadManager.sendText(text, viewModelScope)\n    }\n\n    fun sendUri(uri: Uri): LiveData<Resource<Any?>> {\n        return threadManager.sendUri(uri, viewModelScope)\n    }\n\n    fun startRecording(): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        voiceRecorder = VoiceRecorder(recordingsDir!!, object : VoiceRecorderCallback {\n            override fun onStart() {}\n            override fun onComplete(result: VoiceRecordingResult) {\n                // Log.d(TAG, \"onComplete: recording complete. Scanning file...\");\n                MediaUtils.getVoiceInfo(\n                    contentResolver,\n                    result.file.uri,\n                    object : OnInfoLoadListener<VideoInfo?> {\n                        override fun onLoad(videoInfo: VideoInfo?) {\n                            if (videoInfo == null) return\n                            threadManager.sendVoice(\n                                data,\n                                result.file.uri,\n                                result.waveform,\n                                result.samplingFreq,\n                                videoInfo.duration,\n                                result.file.length(),\n                                viewModelScope\n                            )\n                        }\n\n                        override fun onFailure(t: Throwable) {\n                            data.postValue(error(t.message, null))\n                        }\n                    })\n            }\n\n            override fun onCancel() {}\n        })\n        voiceRecorder?.startRecording(contentResolver)\n        return data\n    }\n\n    fun stopRecording(delete: Boolean) {\n        voiceRecorder?.stopRecording(delete)\n        voiceRecorder = null\n    }\n\n    fun sendReaction(item: DirectItem, emoji: Emoji): LiveData<Resource<Any?>> {\n        return threadManager.sendReaction(item, emoji, viewModelScope)\n    }\n\n    fun sendDeleteReaction(itemId: String): LiveData<Resource<Any?>> {\n        return threadManager.sendDeleteReaction(itemId, viewModelScope)\n    }\n\n    fun unsend(item: DirectItem): LiveData<Resource<Any?>> {\n        return threadManager.unsend(item, viewModelScope)\n    }\n\n    fun sendAnimatedMedia(giphyGif: GiphyGif): LiveData<Resource<Any?>> {\n        return threadManager.sendAnimatedMedia(giphyGif, viewModelScope)\n    }\n\n    fun getUser(userId: Long): User? {\n        var match: User? = null\n        users.value?.let { match = it.firstOrNull { user -> user.pk == userId } }\n        if (match == null) {\n            leftUsers.value?.let { match = it.firstOrNull { user -> user.pk == userId } }\n        }\n        return match\n    }\n\n    fun forward(recipients: Set<RankedRecipient>, itemToForward: DirectItem) {\n        threadManager.forward(recipients, itemToForward, viewModelScope)\n    }\n\n    fun forward(recipient: RankedRecipient, itemToForward: DirectItem) {\n        threadManager.forward(recipient, itemToForward, viewModelScope)\n    }\n\n    fun setReplyToItem(item: DirectItem?) {\n        // Log.d(TAG, \"setReplyToItem: \" + item);\n        threadManager.setReplyToItem(item)\n    }\n\n    fun acceptRequest(): LiveData<Resource<Any?>> {\n        return threadManager.acceptRequest(viewModelScope)\n    }\n\n    fun declineRequest(): LiveData<Resource<Any?>> {\n        return threadManager.declineRequest(viewModelScope)\n    }\n\n    fun markAsSeen(): LiveData<Resource<Any?>> {\n        val thread = thread.value ?: return successEventResObjectLiveData\n        val items = thread.items\n        if (items.isNullOrEmpty()) return successEventResObjectLiveData\n        val directItem = items.firstOrNull { (_, userId) -> userId != currentUser.pk } ?: return successEventResObjectLiveData\n        val lastSeenAt = thread.lastSeenAt ?: return threadManager.markAsSeen(directItem, viewModelScope)\n        val seenAt = lastSeenAt[currentUser.pk] ?: return threadManager.markAsSeen(directItem, viewModelScope)\n        try {\n            val timestamp = seenAt.timestamp ?: return threadManager.markAsSeen(directItem, viewModelScope)\n            val itemIdMatches = seenAt.itemId == directItem.itemId\n            val timestampMatches = timestamp.toLong() >= directItem.getTimestamp()\n            if (itemIdMatches || timestampMatches) {\n                return successEventResObjectLiveData\n            }\n            return threadManager.markAsSeen(directItem, viewModelScope)\n        } catch (ignored: Exception) {\n            return successEventResObjectLiveData\n        }\n    }\n\n    private val successEventResObjectLiveData: MutableLiveData<Resource<Any?>>\n        get() {\n            val data = MutableLiveData<Resource<Any?>>()\n            data.postValue(success(Any()))\n            return data\n        }\n\n    fun deleteThreadIfRequired() {\n        val thread = thread.value ?: return\n        if (thread.isTemp && thread.items.isNullOrEmpty()) {\n            val inboxManager = inboxManager\n            inboxManager.removeThread(threadId)\n        }\n    }\n\n    init {\n        val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n        viewerId = getUserIdFromCookie(cookie)\n        val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n        val csrfToken = getCsrfTokenFromCookie(cookie)\n        require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { \"User is not logged in!\" }\n        threadManager = DirectMessagesManager.getThreadManager(threadId, pending, currentUser, contentResolver)\n        threadManager.fetchPendingRequests(viewModelScope)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/DirectorySelectActivityViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.app.Application\nimport android.content.Intent\nimport android.content.UriPermission\nimport android.net.Uri\nimport android.os.Parcelable\nimport androidx.documentfile.provider.DocumentFile\nimport androidx.lifecycle.AndroidViewModel\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport awais.instagrabber.R\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.utils.Utils\nimport java.io.UnsupportedEncodingException\nimport java.net.URLDecoder\nimport java.nio.charset.StandardCharsets\n\nclass DirectorySelectActivityViewModel(application: Application) : AndroidViewModel(application) {\n    private val _message = MutableLiveData<String>()\n    private val _prevUri = MutableLiveData<String?>()\n    private val _loading = MutableLiveData(false)\n    private val _dirSuccess = MutableLiveData(false)\n\n    val message: LiveData<String> = _message\n    val prevUri: LiveData<String?> = _prevUri\n    val loading: LiveData<Boolean> = _loading\n    val dirSuccess: LiveData<Boolean> = _dirSuccess\n\n    fun setInitialUri(intent: Intent?) {\n        if (intent == null) {\n            setMessage(null)\n            return\n        }\n        val initialUriParcelable = intent.getParcelableExtra<Parcelable>(Constants.EXTRA_INITIAL_URI)\n        if (initialUriParcelable !is Uri) {\n            setMessage(null)\n            return\n        }\n        setMessage(initialUriParcelable as Uri?)\n    }\n\n    private fun setMessage(initialUri: Uri?) {\n        if (initialUri == null) {\n            val prevVersionFolderPath = Utils.settingsHelper.getString(PreferenceKeys.FOLDER_PATH)\n            if (isEmpty(prevVersionFolderPath)) {\n                // default message\n                _message.postValue(getApplication<Application>().getString(R.string.dir_select_default_message))\n                _prevUri.postValue(null)\n                return\n            }\n            _message.postValue(getApplication<Application>().getString(R.string.dir_select_reselect_message))\n            _prevUri.postValue(prevVersionFolderPath)\n            return\n        }\n        val existingPermissions = getApplication<Application>().contentResolver.persistedUriPermissions\n        val anyMatch = existingPermissions.stream().anyMatch { uriPermission: UriPermission -> uriPermission.uri == initialUri }\n        val documentFile = DocumentFile.fromSingleUri(getApplication(), initialUri)\n        val path: String = try {\n            URLDecoder.decode(initialUri.toString(), StandardCharsets.UTF_8.toString())\n        } catch (e: UnsupportedEncodingException) {\n            initialUri.toString()\n        }\n        if (!anyMatch) {\n            _message.postValue(getApplication<Application>().getString(R.string.dir_select_permission_revoked_message))\n            _prevUri.postValue(path)\n            return\n        }\n        if (documentFile == null || !documentFile.exists() || documentFile.lastModified() == 0L) {\n            _message.postValue(getApplication<Application>().getString(R.string.dir_select_folder_not_exist))\n            _prevUri.postValue(path)\n        }\n    }\n\n    @Throws(ReselectDocumentTreeException::class)\n    fun setupSelectedDir(data: Intent) {\n        _loading.postValue(true)\n        try {\n            Utils.setupSelectedDir(getApplication(), data)\n            _message.postValue(getApplication<Application>().getString(R.string.dir_select_success_message))\n            _dirSuccess.postValue(true)\n        } finally {\n            _loading.postValue(false)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/FavoritesViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.app.Application\nimport android.util.Log\nimport androidx.lifecycle.AndroidViewModel\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.db.repositories.FavoriteRepository\nimport awais.instagrabber.utils.extensions.TAG\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\n\nclass FavoritesViewModel(application: Application) : AndroidViewModel(application) {\n    private val _list = MutableLiveData<List<Favorite>>()\n    val list: LiveData<List<Favorite>> = _list\n\n    private val favoriteRepository: FavoriteRepository = FavoriteRepository.getInstance(application)\n\n    init {\n        fetch()\n    }\n\n    fun fetch() {\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                _list.postValue(favoriteRepository.getAllFavorites())\n            } catch (e: Exception) {\n                Log.e(TAG, \"fetch: \", e)\n            }\n        }\n    }\n\n    fun delete(favorite: Favorite, onSuccess: () -> Unit) {\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                favoriteRepository.deleteFavorite(favorite.query, favorite.type)\n                withContext(Dispatchers.Main) { onSuccess() }\n                _list.postValue(favoriteRepository.getAllFavorites())\n            } catch (e: Exception) {\n                Log.e(TAG, \"delete: \", e)\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/FeedStoriesViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.stories.Story;\n\npublic class FeedStoriesViewModel extends ViewModel {\n    private MutableLiveData<List<Story>> list;\n\n    public MutableLiveData<List<Story>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/FileListViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.io.File;\nimport java.util.List;\n\npublic class FileListViewModel extends ViewModel {\n    private MutableLiveData<List<File>> list;\n\n    public MutableLiveData<List<File>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/FiltersFragmentViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\npublic class FiltersFragmentViewModel extends ViewModel {\n    private final MutableLiveData<Boolean> loading = new MutableLiveData<>(false);\n    private final MutableLiveData<ImageEditViewModel.Tab> currentTab = new MutableLiveData<>();\n\n    public FiltersFragmentViewModel() {\n    }\n\n    public LiveData<Boolean> isLoading() {\n        return loading;\n    }\n\n    public LiveData<ImageEditViewModel.Tab> getCurrentTab() {\n        return currentTab;\n    }\n\n    public void setCurrentTab(final ImageEditViewModel.Tab tab) {\n        if (tab == null) return;\n        currentTab.postValue(tab);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport androidx.lifecycle.*\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.webservices.FriendshipRepository\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass FollowViewModel : ViewModel() {\n    // data\n    val userId = MutableLiveData<Long>()\n    private val followers = MutableLiveData<List<User>>()\n    private val followings = MutableLiveData<List<User>>()\n    private val searchResults = MutableLiveData<List<User>>()\n\n    // cursors\n    private val followersMaxId = MutableLiveData<String?>(\"\")\n    private val followingMaxId = MutableLiveData<String?>(\"\")\n    private val searchingMaxId = MutableLiveData<String?>(\"\")\n    private val searchQuery = MutableLiveData<String?>()\n\n    // comparison\n    val status: LiveData<Pair<Boolean, Boolean>> = object : MediatorLiveData<Pair<Boolean, Boolean>>() {\n            init {\n                postValue(Pair(false, false))\n                addSource(followersMaxId) {\n                    if (it == null) {\n                        postValue(Pair(true, value!!.second))\n                    }\n                    else fetch(true, it)\n                }\n                addSource(followingMaxId) {\n                    if (it == null) {\n                        postValue(Pair(value!!.first, true))\n                    }\n                    else fetch(false, it)\n                }\n            }\n        }\n    val comparison: LiveData<Triple<List<User>, List<User>, List<User>>> =\n        object : MediatorLiveData<Triple<List<User>, List<User>, List<User>>>() {\n            init {\n                addSource(status) {\n                    if (it.first && it.second) {\n                        val followersList = followers.value!!\n                        val followingList = followings.value!!\n                        val allUsers: MutableList<User> = mutableListOf()\n                        allUsers.addAll(followersList)\n                        allUsers.addAll(followingList)\n                        val followersMap = followersList.groupBy { it.pk }\n                        val followingMap = followingList.groupBy { it.pk }\n                        val mutual: MutableList<User> = mutableListOf()\n                        val onlyFollowing: MutableList<User> = mutableListOf()\n                        val onlyFollowers: MutableList<User> = mutableListOf()\n                        allUsers.forEach {\n                            val isFollowing = followingMap.get(it.pk) != null\n                            val isFollower = followersMap.get(it.pk) != null\n                            if (isFollowing && isFollower) mutual.add(it)\n                            else if (isFollowing) onlyFollowing.add(it)\n                            else if (isFollower) onlyFollowers.add(it)\n                        }\n                        postValue(Triple(mutual, onlyFollowing, onlyFollowers))\n                    }\n                }\n            }\n        }\n\n    private val friendshipRepository: FriendshipRepository by lazy { FriendshipRepository.getInstance() }\n\n    // fetch: supply max ID for continuous fetch\n    fun fetch(follower: Boolean, nextMaxId: String?): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(Resource.loading(null))\n        val maxId = if (follower) followersMaxId else followingMaxId\n        if (maxId.value == null && nextMaxId == null) data.postValue(Resource.success(null))\n        else if (userId.value == null) data.postValue(Resource.error(\"No user ID supplied!\", null))\n        else viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val tempList = friendshipRepository.getList(\n                    follower,\n                    userId.value!!,\n                    nextMaxId ?: maxId.value,\n                    null\n                )\n                if (!tempList.status.equals(\"ok\")) {\n                    data.postValue(Resource.error(\"Status not ok!\", null))\n                }\n                else {\n                    if (tempList.users != null) {\n                        val liveData = if (follower) followers else followings\n                        val currentList = if (liveData.value != null) liveData.value!!.toMutableList()\n                                          else mutableListOf()\n                        currentList.addAll(tempList.users!!)\n                        liveData.postValue(currentList.toList())\n                    }\n                    maxId.postValue(tempList.nextMaxId)\n                    data.postValue(Resource.success(null))\n                }\n            } catch (e: Exception) {\n                data.postValue(Resource.error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun getList(follower: Boolean): LiveData<List<User>> {\n        return if (follower) followers else followings\n    }\n\n    fun search(follower: Boolean): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(Resource.loading(null))\n        val query = searchQuery.value\n        if (searchingMaxId.value == null) data.postValue(Resource.success(null))\n        else if (userId.value == null) data.postValue(Resource.error(\"No user ID supplied!\", null))\n        else if (query.isNullOrEmpty()) data.postValue(Resource.error(\"No query supplied!\", null))\n        else viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val tempList = friendshipRepository.getList(\n                    follower,\n                    userId.value!!,\n                    searchingMaxId.value,\n                    query\n                )\n                if (!tempList.status.equals(\"ok\")) {\n                    data.postValue(Resource.error(\"Status not ok!\", null))\n                }\n                else {\n                    if (tempList.users != null) {\n                        val currentList = if (searchResults.value != null) searchResults.value!!.toMutableList()\n                                          else mutableListOf()\n                        currentList.addAll(tempList.users!!)\n                        searchResults.postValue(currentList.toList())\n                    }\n                    searchingMaxId.postValue(tempList.nextMaxId)\n                    data.postValue(Resource.success(null))\n                }\n            } catch (e: Exception) {\n                data.postValue(Resource.error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun getSearch(): LiveData<List<User>> {\n        return searchResults\n    }\n\n    fun setQuery(query: String?, follower: Boolean) {\n        searchQuery.value = query\n        if (!query.isNullOrEmpty()) search(follower)\n    }\n\n    fun clearProgress() {\n        followersMaxId.value = \"\"\n        followingMaxId.value = \"\"\n        searchingMaxId.value = \"\"\n        followings.value = listOf<User>()\n        followers.value = listOf<User>()\n        searchResults.value = listOf<User>()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/GifPickerViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGifImages;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGifResults;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.webservices.GifService;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class GifPickerViewModel extends ViewModel {\n    private static final String TAG = GifPickerViewModel.class.getSimpleName();\n\n    private final MutableLiveData<Resource<List<GiphyGif>>> images = new MutableLiveData<>(Resource.success(Collections.emptyList()));\n    private final GifService gifService;\n\n    private Call<GiphyGifResponse> searchRequest;\n\n    public GifPickerViewModel() {\n        gifService = GifService.getInstance();\n        search(null);\n    }\n\n    public LiveData<Resource<List<GiphyGif>>> getImages() {\n        return images;\n    }\n\n    public void search(final String query) {\n        final Resource<List<GiphyGif>> currentValue = images.getValue();\n        if (currentValue != null && currentValue.status == Resource.Status.LOADING) {\n            cancelSearchRequest();\n        }\n        images.postValue(Resource.loading(getCurrentImages()));\n        searchRequest = gifService.searchGiphyGifs(query, query != null);\n        searchRequest.enqueue(new Callback<GiphyGifResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<GiphyGifResponse> call,\n                                   @NonNull final Response<GiphyGifResponse> response) {\n                if (response.isSuccessful()) {\n                    parseResponse(response);\n                    return;\n                }\n                if (response.errorBody() != null) {\n                    try {\n                        final String string = response.errorBody().string();\n                        final String msg = String.format(Locale.US,\n                                                         \"onResponse: url: %s, responseCode: %d, errorBody: %s\",\n                                                         call.request().url().toString(),\n                                                         response.code(),\n                                                         string);\n                        images.postValue(Resource.error(msg, getCurrentImages()));\n                        Log.e(TAG, msg);\n                    } catch (IOException e) {\n                        images.postValue(Resource.error(e.getMessage(), getCurrentImages()));\n                        Log.e(TAG, \"onResponse: \", e);\n                    }\n                }\n                images.postValue(Resource.error(R.string.generic_failed_request, getCurrentImages()));\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<GiphyGifResponse> call,\n                                  @NonNull final Throwable t) {\n                images.postValue(Resource.error(t.getMessage(), getCurrentImages()));\n                Log.e(TAG, \"enqueueRequest: onFailure: \", t);\n            }\n        });\n    }\n\n    private void parseResponse(final Response<GiphyGifResponse> response) {\n        final GiphyGifResponse giphyGifResponse = response.body();\n        if (giphyGifResponse == null) {\n            images.postValue(Resource.error(R.string.generic_null_response, getCurrentImages()));\n            return;\n        }\n        final GiphyGifResults results = giphyGifResponse.getResults();\n        images.postValue(Resource.success(\n                ImmutableList.<GiphyGif>builder()\n                        .addAll(results.getGiphy() == null ? Collections.emptyList() : filterInvalid(results.getGiphy()))\n                        .addAll(results.getGiphyGifs() == null ? Collections.emptyList() : filterInvalid(results.getGiphyGifs()))\n                        .build()\n        ));\n    }\n\n    private List<GiphyGif> filterInvalid(@NonNull final List<GiphyGif> giphyGifs) {\n        return giphyGifs.stream()\n                        .filter(Objects::nonNull)\n                        .filter(giphyGif -> {\n                            final GiphyGifImages images = giphyGif.getImages();\n                            if (images == null) return false;\n                            final AnimatedMediaFixedHeight fixedHeight = images.getFixedHeight();\n                            if (fixedHeight == null) return false;\n                            return !TextUtils.isEmpty(fixedHeight.getWebp());\n                        })\n                        .collect(Collectors.toList());\n    }\n\n    // @NonNull\n    // private List<GiphyGifImage> getGiphyGifImages(@NonNull final List<GiphyGif> giphy) {\n    //     return giphy.stream()\n    //                 .map(giphyGif -> {\n    //                     final GiphyGifImages images = giphyGif.getImages();\n    //                     if (images == null) return null;\n    //                     return images.getOriginal();\n    //                 })\n    //                 .filter(Objects::nonNull)\n    //                 .collect(Collectors.toList());\n    // }\n\n    private List<GiphyGif> getCurrentImages() {\n        final Resource<List<GiphyGif>> value = images.getValue();\n        return value == null ? Collections.emptyList() : value.data;\n    }\n\n    public void cancelSearchRequest() {\n        if (searchRequest == null) return;\n        searchRequest.cancel();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/ImageEditViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.app.Application;\nimport android.graphics.RectF;\nimport android.net.Uri;\n\nimport androidx.annotation.NonNull;\nimport androidx.documentfile.provider.DocumentFile;\nimport androidx.lifecycle.AndroidViewModel;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\n\nimport java.io.File;\nimport java.time.format.DateTimeFormatter;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\nimport awais.instagrabber.fragments.imageedit.filters.FiltersHelper.FilterType;\nimport awais.instagrabber.fragments.imageedit.filters.filters.Filter;\nimport awais.instagrabber.fragments.imageedit.filters.properties.Property;\nimport awais.instagrabber.models.SavedImageEditState;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.DownloadUtils;\nimport awais.instagrabber.utils.SerializablePair;\nimport awais.instagrabber.utils.Utils;\nimport jp.co.cyberagent.android.gpuimage.GPUImage;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;\nimport jp.co.cyberagent.android.gpuimage.filter.GPUImageFilterGroup;\n\npublic class ImageEditViewModel extends AndroidViewModel {\n    private static final String CROP = \"crop\";\n    private static final String RESULT = \"result\";\n    private static final String FILE_FORMAT = \"yyyyMMddHHmmssSSS\";\n    private static final String MIME_TYPE = Utils.mimeTypeMap.getMimeTypeFromExtension(\"jpg\");\n    private static final DateTimeFormatter SIMPLE_DATE_FORMAT = DateTimeFormatter.ofPattern(FILE_FORMAT, Locale.US);\n\n    private Uri originalUri;\n    private SavedImageEditState savedImageEditState;\n\n    private final String sessionId;\n    private final Uri destinationUri;\n    private final Uri cropDestinationUri;\n    private final MutableLiveData<Boolean> loading = new MutableLiveData<>(false);\n    private final MutableLiveData<Uri> resultUri = new MutableLiveData<>(null);\n    private final MutableLiveData<Tab> currentTab = new MutableLiveData<>(Tab.RESULT);\n    private final MutableLiveData<Boolean> isCropped = new MutableLiveData<>(false);\n    private final MutableLiveData<Boolean> isTuned = new MutableLiveData<>(false);\n    private final MutableLiveData<Boolean> isFiltered = new MutableLiveData<>(false);\n    private final DocumentFile outputDir;\n    private List<Filter<? extends GPUImageFilter>> tuningFilters;\n    private Filter<? extends GPUImageFilter> appliedFilter;\n    private final DocumentFile destinationFile;\n\n    public ImageEditViewModel(final Application application) {\n        super(application);\n        sessionId = LocalDateTime.now().format(SIMPLE_DATE_FORMAT);\n        outputDir = DownloadUtils.getImageEditDir(sessionId, application);\n        destinationFile = outputDir.createFile(MIME_TYPE, RESULT + \".jpg\");\n        destinationUri = destinationFile.getUri();\n        cropDestinationUri = outputDir.createFile(MIME_TYPE, CROP + \".jpg\").getUri();\n    }\n\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    public Uri getOriginalUri() {\n        return originalUri;\n    }\n\n    public void setOriginalUri(final Uri originalUri) {\n        if (originalUri == null) return;\n        this.originalUri = originalUri;\n        savedImageEditState = new SavedImageEditState(sessionId, originalUri.toString());\n        if (resultUri.getValue() == null) {\n            resultUri.postValue(originalUri);\n        }\n    }\n\n    public Uri getDestinationUri() {\n        return destinationUri;\n    }\n\n    public Uri getCropDestinationUri() {\n        return cropDestinationUri;\n    }\n\n    public LiveData<Boolean> isLoading() {\n        return loading;\n    }\n\n    public LiveData<Uri> getResultUri() {\n        return resultUri;\n    }\n\n    public LiveData<Boolean> isCropped() {\n        return isCropped;\n    }\n\n    public LiveData<Boolean> isTuned() {\n        return isTuned;\n    }\n\n    public LiveData<Boolean> isFiltered() {\n        return isFiltered;\n    }\n\n    public void setResultUri(final Uri uri) {\n        if (uri == null) return;\n        resultUri.postValue(uri);\n    }\n\n    public LiveData<Tab> getCurrentTab() {\n        return currentTab;\n    }\n\n    public void setCurrentTab(final Tab tab) {\n        if (tab == null) return;\n        this.currentTab.postValue(tab);\n    }\n\n    public SavedImageEditState getSavedImageEditState() {\n        return savedImageEditState;\n    }\n\n    public void setCropResult(final float[] imageMatrixValues, final RectF cropRect) {\n        savedImageEditState.setCropImageMatrixValues(imageMatrixValues);\n        savedImageEditState.setCropRect(cropRect);\n        isCropped.postValue(true);\n        applyFilters();\n    }\n\n    private void applyFilters() {\n        final GPUImage gpuImage = new GPUImage(getApplication());\n        if ((tuningFilters != null && !tuningFilters.isEmpty()) || appliedFilter != null) {\n            AppExecutors.INSTANCE.getTasksThread().submit(() -> {\n                final List<GPUImageFilter> list = new ArrayList<>();\n                if (tuningFilters != null) {\n                    for (Filter<? extends GPUImageFilter> tuningFilter : tuningFilters) {\n                        list.add(tuningFilter.getInstance());\n                    }\n                }\n                if (appliedFilter != null) {\n                    list.add(appliedFilter.getInstance());\n                }\n                gpuImage.setFilter(new GPUImageFilterGroup(list));\n                final Uri uri = cropDestinationUri != null ? cropDestinationUri : originalUri;\n                gpuImage.setImage(uri);\n                gpuImage.saveToPictures(new File(destinationUri.toString()), false, uri1 -> setResultUri(destinationUri));\n            });\n            return;\n        }\n        setResultUri(cropDestinationUri);\n    }\n\n    public void cancel() {\n        delete(outputDir);\n    }\n\n    private void delete(@NonNull final DocumentFile file) {\n        if (file.isDirectory()) {\n            final DocumentFile[] files = file.listFiles();\n            if (files != null) {\n                for (DocumentFile f : files) {\n                    delete(f);\n                }\n            }\n        }\n        file.delete();\n    }\n\n    public void setAppliedFilters(final List<Filter<?>> tuningFilters, final Filter<?> filter) {\n        this.tuningFilters = tuningFilters;\n        this.appliedFilter = filter;\n        if (savedImageEditState != null) {\n            final HashMap<FilterType, Map<Integer, Object>> tuningFiltersMap = new HashMap<>();\n            for (final Filter<?> tuningFilter : tuningFilters) {\n                final SerializablePair<FilterType, Map<Integer, Object>> filterValuesMap = getFilterValuesMap(tuningFilter);\n                tuningFiltersMap.put(filterValuesMap.first, filterValuesMap.second);\n            }\n            savedImageEditState.setAppliedTuningFilters(tuningFiltersMap);\n            savedImageEditState.setAppliedFilter(getFilterValuesMap(filter));\n        }\n        isTuned.postValue(!tuningFilters.isEmpty());\n        isFiltered.postValue(filter != null);\n        setResultUri(destinationUri);\n    }\n\n    private SerializablePair<FilterType, Map<Integer, Object>> getFilterValuesMap(final Filter<?> filter) {\n        if (filter == null) return null;\n        final FilterType type = filter.getType();\n        final Map<Integer, Property<?>> properties = filter.getProperties();\n        final Map<Integer, Object> propertyValueMap = new HashMap<>();\n        if (properties != null) {\n            final Set<Map.Entry<Integer, Property<?>>> entries = properties.entrySet();\n            for (final Map.Entry<Integer, Property<?>> entry : entries) {\n                final Integer propId = entry.getKey();\n                final Property<?> property = entry.getValue();\n                final Object value = property.getValue();\n                propertyValueMap.put(propId, value);\n            }\n        }\n        return new SerializablePair<>(type, propertyValueMap);\n    }\n\n    // public File getDestinationFile() {\n    //     return destinationFile;\n    // }\n\n    public enum Tab {\n        RESULT,\n        CROP,\n        TUNE,\n        FILTERS\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/MediaViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\nimport androidx.lifecycle.ViewModelProvider;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport awais.instagrabber.customviews.helpers.PostFetcher;\nimport awais.instagrabber.fragments.settings.PreferenceKeys;\nimport awais.instagrabber.interfaces.FetchListener;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.KeywordsFilterUtilsKt;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class MediaViewModel extends ViewModel {\n    private static final String TAG = MediaViewModel.class.getSimpleName();\n\n    private boolean refresh = true;\n\n    private final PostFetcher postFetcher;\n    private final MutableLiveData<List<Media>> list = new MutableLiveData<>();\n\n    public MediaViewModel(@NonNull final PostFetcher.PostFetchService postFetchService) {\n        final FetchListener<List<Media>> fetchListener = new FetchListener<List<Media>>() {\n            @Override\n            public void onResult(final List<Media> result) {\n                if (refresh) {\n                    list.postValue(filterResult(result, true));\n                    refresh = false;\n                    return;\n                }\n                list.postValue(filterResult(result, false));\n            }\n\n            @Override\n            public void onFailure(final Throwable t) {\n                Log.e(TAG, \"onFailure: \", t);\n            }\n        };\n        postFetcher = new PostFetcher(postFetchService, fetchListener);\n    }\n\n    @NonNull\n    private List<Media> filterResult(final List<Media> result, final boolean isRefresh) {\n        final List<Media> models = list.getValue();\n        final List<Media> modelsCopy = models == null || isRefresh ? new ArrayList<>() : new ArrayList<>(models);\n        if (settingsHelper.getBoolean(PreferenceKeys.TOGGLE_KEYWORD_FILTER)) {\n            final List<String> keywords = new ArrayList<>(settingsHelper.getStringSet(PreferenceKeys.KEYWORD_FILTERS));\n            final List<Media> filter = KeywordsFilterUtilsKt.filter(keywords, result);\n            if (filter != null) {\n                modelsCopy.addAll(filter);\n            }\n            return modelsCopy;\n        }\n        modelsCopy.addAll(result);\n        return modelsCopy;\n    }\n\n    public LiveData<List<Media>> getList() {\n        return list;\n    }\n\n    public boolean hasMore() {\n        return postFetcher.hasMore();\n    }\n\n    public void fetch() {\n        postFetcher.fetch();\n    }\n\n    public void reset() {\n        postFetcher.reset();\n    }\n\n    public boolean isFetching() {\n        return postFetcher.isFetching();\n    }\n\n    public void refresh() {\n        refresh = true;\n        reset();\n        fetch();\n    }\n\n    public static class ViewModelFactory implements ViewModelProvider.Factory {\n\n        @NonNull\n        private final PostFetcher.PostFetchService postFetchService;\n\n        public ViewModelFactory(@NonNull final PostFetcher.PostFetchService postFetchService) {\n            this.postFetchService = postFetchService;\n        }\n\n        @NonNull\n        @Override\n        public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {\n            //noinspection unchecked\n            return (T) new MediaViewModel(postFetchService);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.notification.Notification;\n\npublic class NotificationViewModel extends ViewModel {\n    private MutableLiveData<List<Notification>> list;\n\n    public MutableLiveData<List<Notification>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/PostViewV2ViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.util.Log\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.R\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.repositories.responses.Caption\nimport awais.instagrabber.repositories.responses.Location\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.utils.getCsrfTokenFromCookie\nimport awais.instagrabber.utils.getUserIdFromCookie\nimport awais.instagrabber.webservices.MediaRepository\nimport com.google.common.collect.ImmutableList\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport java.util.*\n\nclass PostViewV2ViewModel : ViewModel() {\n    private val user = MutableLiveData<User?>()\n    private val caption = MutableLiveData<Caption?>()\n    private val location = MutableLiveData<Location?>()\n    private val date = MutableLiveData<String>()\n    private val likeCount = MutableLiveData(0L)\n    private val commentCount = MutableLiveData(0L)\n    private val viewCount = MutableLiveData(0L)\n    private val type = MutableLiveData<MediaItemType?>()\n    private val liked = MutableLiveData(false)\n    private val saved = MutableLiveData(false)\n    private val options = MutableLiveData<List<Int>>(ArrayList())\n    private var messageManager: DirectMessagesManager? = null\n    private val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n    private val deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n    private val csrfToken = getCsrfTokenFromCookie(cookie)\n    private val viewerId = getUserIdFromCookie(cookie)\n    private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }\n\n    lateinit var media: Media\n        private set\n    val isLoggedIn = cookie.isNotBlank() && !csrfToken.isNullOrBlank() && viewerId != 0L\n\n    fun setMedia(media: Media) {\n        this.media = media\n        user.postValue(media.user)\n        caption.postValue(media.caption)\n        location.postValue(media.location)\n        date.postValue(media.date)\n        likeCount.postValue(media.likeCount)\n        commentCount.postValue(media.commentCount)\n        viewCount.postValue(if (media.type == MediaItemType.MEDIA_TYPE_VIDEO) media.viewCount else null)\n        type.postValue(media.type)\n        liked.postValue(media.hasLiked)\n        saved.postValue(media.hasViewerSaved)\n        initOptions()\n    }\n\n    private fun initOptions() {\n        val builder = ImmutableList.builder<Int>()\n        val user1 = media.user\n        if (isLoggedIn && user1 != null && user1.pk == viewerId) {\n            builder.add(R.id.edit_caption)\n            builder.add(R.id.delete)\n        }\n        options.postValue(builder.build())\n    }\n\n    fun getUser(): LiveData<User?> {\n        return user\n    }\n\n    fun getCaption(): LiveData<Caption?> {\n        return caption\n    }\n\n    fun getLocation(): LiveData<Location?> {\n        return location\n    }\n\n    fun getDate(): LiveData<String> {\n        return date\n    }\n\n    fun getLikeCount(): LiveData<Long> {\n        return likeCount\n    }\n\n    fun getCommentCount(): LiveData<Long> {\n        return commentCount\n    }\n\n    fun getViewCount(): LiveData<Long?> {\n        return viewCount\n    }\n\n    fun getType(): LiveData<MediaItemType?> {\n        return type\n    }\n\n    fun getLiked(): LiveData<Boolean> {\n        return liked\n    }\n\n    fun getSaved(): LiveData<Boolean> {\n        return saved\n    }\n\n    fun getOptions(): LiveData<List<Int>> {\n        return options\n    }\n\n    fun toggleLike(): LiveData<Resource<Any?>> {\n        return if (media.hasLiked) {\n            unlike()\n        } else like()\n    }\n\n    fun like(): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val mediaId = media.pk ?: return@launch\n                val liked = mediaRepository.like(csrfToken!!, viewerId, deviceUuid, mediaId)\n                updateMediaLikeUnlike(data, liked)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unlike(): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val mediaId = media.pk ?: return@launch\n                val unliked = mediaRepository.unlike(csrfToken!!, viewerId, deviceUuid, mediaId)\n                updateMediaLikeUnlike(data, unliked)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    private fun updateMediaLikeUnlike(data: MutableLiveData<Resource<Any?>>, result: Boolean) {\n        if (!result) {\n            data.postValue(error(\"\", null))\n            return\n        }\n        data.postValue(success(true))\n        val currentLikesCount = media.likeCount\n        val updatedCount: Long\n        if (!media.hasLiked) {\n            updatedCount = currentLikesCount + 1\n            media.hasLiked = true\n        } else {\n            updatedCount = currentLikesCount - 1\n            media.hasLiked = false\n        }\n        media.likeCount = updatedCount\n        likeCount.postValue(updatedCount)\n        liked.postValue(media.hasLiked)\n    }\n\n    fun toggleSave(): LiveData<Resource<Any?>> {\n        return if (!media.hasViewerSaved) {\n            save(null, false)\n        } else unsave()\n    }\n\n    fun toggleSave(collection: String?, ignoreSaveState: Boolean): LiveData<Resource<Any?>> {\n        return save(collection, ignoreSaveState)\n    }\n\n    fun save(collection: String?, ignoreSaveState: Boolean): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val mediaId = media.pk ?: return@launch\n                val saved = mediaRepository.save(csrfToken!!, viewerId, deviceUuid, mediaId, collection)\n                getSaveUnsaveCallback(data, saved, ignoreSaveState)\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun unsave(): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            val mediaId = media.pk ?: return@launch\n            val unsaved = mediaRepository.unsave(csrfToken!!, viewerId, deviceUuid, mediaId)\n            getSaveUnsaveCallback(data, unsaved, false)\n        }\n        return data\n    }\n\n    private fun getSaveUnsaveCallback(\n        data: MutableLiveData<Resource<Any?>>,\n        result: Boolean,\n        ignoreSaveState: Boolean,\n    ) {\n        if (!result) {\n            data.postValue(error(\"\", null))\n            return\n        }\n        data.postValue(success(true))\n        if (!ignoreSaveState) media.hasViewerSaved = !media.hasViewerSaved\n        saved.postValue(media.hasViewerSaved)\n    }\n\n    fun updateCaption(caption: String): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val postId = media.pk ?: return@launch\n                val result = mediaRepository.editCaption(csrfToken!!, viewerId, deviceUuid, postId, caption)\n                if (result) {\n                    data.postValue(success(\"\"))\n                    media.setPostCaption(caption)\n                    this@PostViewV2ViewModel.caption.postValue(media.caption)\n                    return@launch\n                }\n                data.postValue(error(\"\", null))\n            } catch (e: Exception) {\n                Log.e(TAG, \"Error editing caption\", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun translateCaption(): LiveData<Resource<String?>> {\n        val data = MutableLiveData<Resource<String?>>()\n        data.postValue(loading(null))\n        val value = caption.value\n        val pk = value?.pk\n        if (pk == null) {\n            data.postValue(error(\"caption is null\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val result = mediaRepository.translate(pk, \"1\") ?: return@launch\n                if (result.isBlank()) {\n                    // data.postValue(error(\"\", null))\n                    return@launch\n                }\n                data.postValue(success(result))\n            } catch (e: Exception) {\n                Log.e(TAG, \"Error translating comment\", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun hasPk(): Boolean {\n        return media.pk != null\n    }\n\n    fun setViewCount(viewCount: Long?) {\n        this.viewCount.postValue(viewCount)\n    }\n\n    fun delete(): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        if (!isLoggedIn) {\n            data.postValue(error(\"Not logged in!\", null))\n            return data\n        }\n        val mediaId = media.id\n        val mediaType = media.type\n        if (mediaId == null || mediaType == null) {\n            data.postValue(error(\"media id or type is null\", null))\n            return data\n        }\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val response = mediaRepository.delete(csrfToken!!, viewerId, deviceUuid, mediaId, mediaType)\n                if (response == null) {\n                    data.postValue(success(Any()))\n                    return@launch\n                }\n                data.postValue(success(Any()))\n            } catch (e: Exception) {\n                Log.e(TAG, \"delete: \", e)\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun shareDm(result: RankedRecipient, child: Int) {\n        if (messageManager == null) {\n            messageManager = DirectMessagesManager\n        }\n        val mediaId = media.id ?: return\n        val childId = if (child == -1) null else media.carouselMedia?.get(child)?.id\n        messageManager?.sendMedia(result, mediaId, childId, BroadcastItemType.MEDIA_SHARE, viewModelScope)\n    }\n\n    fun shareDm(recipients: Set<RankedRecipient>, child: Int) {\n        if (messageManager == null) {\n            messageManager = DirectMessagesManager\n        }\n        val mediaId = media.id ?: return\n        val childId = if (child == -1) null else media.carouselMedia?.get(child)?.id\n        messageManager?.sendMedia(recipients, mediaId, childId, BroadcastItemType.MEDIA_SHARE, viewModelScope)\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.os.Bundle\nimport android.util.Log\nimport androidx.lifecycle.*\nimport androidx.savedstate.SavedStateRegistryOwner\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.db.repositories.FavoriteRepository\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.FriendshipStatus\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.UserProfileContextLink\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.repositories.responses.stories.Story\nimport awais.instagrabber.utils.ControlledRunner\nimport awais.instagrabber.utils.Event\nimport awais.instagrabber.utils.SingleRunner\nimport awais.instagrabber.utils.extensions.TAG\nimport awais.instagrabber.utils.extensions.isReallyPrivate\nimport awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileAction.*\nimport awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileEvent.*\nimport awais.instagrabber.webservices.*\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport java.time.LocalDateTime\n\nclass ProfileFragmentViewModel(\n    private val state: SavedStateHandle,\n    private val csrfToken: String?,\n    private val deviceUuid: String?,\n    private val userRepository: UserRepository,\n    private val friendshipRepository: FriendshipRepository,\n    private val storiesRepository: StoriesRepository,\n    private val mediaRepository: MediaRepository,\n    private val graphQLRepository: GraphQLRepository,\n    private val favoriteRepository: FavoriteRepository,\n    private val directMessagesRepository: DirectMessagesRepository,\n    private val messageManager: DirectMessagesManager?,\n    ioDispatcher: CoroutineDispatcher,\n) : ViewModel() {\n    private val _currentUser = MutableLiveData<Resource<User?>>(Resource.loading(null))\n    private val _isFavorite = MutableLiveData(false)\n    private val profileAction = MutableLiveData(INIT)\n    private val _eventLiveData = MutableLiveData<Event<ProfileEvent>?>()\n\n    private var previousUsername: String? = null\n\n    enum class ProfileAction {\n        INIT,\n        REFRESH,\n        REFRESH_FRIENDSHIP,\n    }\n\n    sealed class ProfileEvent {\n        object ShowConfirmUnfollowDialog : ProfileEvent()\n        class DMButtonState(val disabled: Boolean) : ProfileEvent()\n        class NavigateToThread(val threadId: String, val username: String) : ProfileEvent()\n        class ShowTranslation(val result: String) : ProfileEvent()\n    }\n\n    val currentUser: LiveData<Resource<User?>> = _currentUser\n    val isLoggedIn: LiveData<Boolean> = currentUser.map { it.data != null }\n    val isFavorite: LiveData<Boolean> = _isFavorite\n    val eventLiveData: LiveData<Event<ProfileEvent>?> = _eventLiveData\n\n    private val currentUserStateUsernameActionLiveData: LiveData<Triple<Resource<User?>, Resource<String?>, ProfileAction>> =\n        object : MediatorLiveData<Triple<Resource<User?>, Resource<String?>, ProfileAction>>() {\n            var user: Resource<User?> = Resource.loading(null)\n            var stateUsername: Resource<String?> = Resource.loading(null)\n            var action: ProfileAction = INIT\n\n            init {\n                addSource(currentUser) { currentUser ->\n                    this.user = currentUser\n                    value = Triple(currentUser, stateUsername, action)\n                }\n                addSource(state.getLiveData<String?>(\"username\")) { username ->\n                    this.stateUsername = Resource.success(username.substringAfter('@'))\n                    value = Triple(user, this.stateUsername, action)\n                }\n                addSource(profileAction) { action ->\n                    this.action = action\n                    value = Triple(user, stateUsername, action)\n                }\n                // trigger currentUserStateUsernameActionLiveData switch map with a state username success resource\n                if (!state.contains(\"username\")) {\n                    this.stateUsername = Resource.success(null)\n                    value = Triple(user, this.stateUsername, action)\n                }\n            }\n        }\n\n    private val profileFetchControlledRunner = ControlledRunner<User?>()\n    val profile: LiveData<Resource<User?>> = currentUserStateUsernameActionLiveData.switchMap {\n        val (currentUserResource, stateUsernameResource, action) = it\n        liveData<Resource<User?>>(context = viewModelScope.coroutineContext + ioDispatcher) {\n            if (action == INIT && previousUsername != null && stateUsernameResource.data == previousUsername) return@liveData\n            if (currentUserResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) {\n                emit(Resource.loading(profileCopy.value?.data))\n                return@liveData\n            }\n            val currentUser = currentUserResource.data\n            val stateUsername = stateUsernameResource.data\n            if (stateUsername.isNullOrBlank()) {\n                emit(Resource.success(currentUser))\n                return@liveData\n            }\n            try {\n                when (action) {\n                    INIT, REFRESH -> {\n                        previousUsername = stateUsername\n                        val fetchedUser = profileFetchControlledRunner.cancelPreviousThenRun { fetchUser(currentUser, stateUsername) }\n                        emit(Resource.success(fetchedUser))\n                        if (fetchedUser != null) {\n                            checkAndUpdateFavorite(fetchedUser)\n                        }\n                    }\n                    REFRESH_FRIENDSHIP -> {\n                        var profile = profileCopy.value?.data ?: return@liveData\n                        profile = profile.copy(friendshipStatus = userRepository.getUserFriendship(profile.pk))\n                        emit(Resource.success(profile))\n                    }\n                }\n            } catch (e: Exception) {\n                emit(Resource.error(e.message, profileCopy.value?.data))\n                Log.e(TAG, \"fetching user: \", e)\n            }\n        }\n    }\n    val profileCopy = profile\n\n    val currentUserProfileActionLiveData: LiveData<Triple<Resource<User?>, Resource<User?>, ProfileAction>> =\n        object : MediatorLiveData<Triple<Resource<User?>, Resource<User?>, ProfileAction>>() {\n            var currentUser: Resource<User?> = Resource.loading(null)\n            var profile: Resource<User?> = Resource.loading(null)\n            var action: ProfileAction = INIT\n\n            init {\n                addSource(this@ProfileFragmentViewModel.currentUser) { currentUser ->\n                    this.currentUser = currentUser\n                    value = Triple(currentUser, profile, action)\n                }\n                addSource(this@ProfileFragmentViewModel.profile) { profile ->\n                    this.profile = profile\n                    value = Triple(currentUser, this.profile, action)\n                }\n                addSource(profileAction) { action ->\n                    this.action = action\n                    value = Triple(currentUser, this.profile, action)\n                }\n            }\n        }\n\n    private val storyFetchControlledRunner = ControlledRunner<Story?>()\n    val userStories: LiveData<Resource<Story?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair ->\n        liveData<Resource<Story?>>(context = viewModelScope.coroutineContext + ioDispatcher) {\n            val (currentUserResource, profileResource, action) = currentUserAndProfilePair\n            if (action != INIT && action != REFRESH) {\n                return@liveData\n            }\n            // don't fetch if not logged in\n            if (currentUserResource.data == null) {\n                emit(Resource.success(null))\n                return@liveData\n            }\n            if (currentUserResource.status == Resource.Status.LOADING || profileResource.status == Resource.Status.LOADING) {\n                emit(Resource.loading(null))\n                return@liveData\n            }\n            val user = profileResource.data\n            if (user == null) {\n                emit(Resource.success(null))\n                return@liveData\n            }\n            try {\n                val fetchedStories = storyFetchControlledRunner.cancelPreviousThenRun { fetchUserStory(user) }\n                emit(Resource.success(fetchedStories))\n            } catch (e: Exception) {\n                emit(Resource.error(e.message, null))\n                Log.e(TAG, \"fetching story: \", e)\n            }\n        }\n    }\n\n    private val highlightsFetchControlledRunner = ControlledRunner<List<Story>?>()\n    val userHighlights: LiveData<Resource<List<Story>?>> = currentUserProfileActionLiveData.switchMap { currentUserAndProfilePair ->\n        liveData<Resource<List<Story>?>>(context = viewModelScope.coroutineContext + ioDispatcher) {\n            val (currentUserResource, profileResource, action) = currentUserAndProfilePair\n            if (action != INIT && action != REFRESH) {\n                return@liveData\n            }\n            // don't fetch if not logged in\n            if (currentUserResource.data == null) {\n                emit(Resource.success(null))\n                return@liveData\n            }\n            if (currentUserResource.status == Resource.Status.LOADING || profileResource.status == Resource.Status.LOADING) {\n                emit(Resource.loading(null))\n                return@liveData\n            }\n            val user = profileResource.data\n            if (user == null) {\n                emit(Resource.success(null))\n                return@liveData\n            }\n            try {\n                val fetchedHighlights = highlightsFetchControlledRunner.cancelPreviousThenRun { fetchUserHighlights(user) }\n                emit(Resource.success(fetchedHighlights))\n            } catch (e: Exception) {\n                emit(Resource.error(e.message, null))\n                Log.e(TAG, \"fetching highlights: \", e)\n            }\n        }\n    }\n\n    private suspend fun fetchUser(\n        currentUser: User?,\n        stateUsername: String,\n    ): User? {\n        if (currentUser != null) {\n            // logged in\n            val tempUser = userRepository.getUsernameInfo(stateUsername)\n            if (!tempUser.isReallyPrivate(currentUser)) {\n                tempUser.friendshipStatus = userRepository.getUserFriendship(tempUser.pk)\n            }\n            return tempUser\n        }\n        // anonymous\n        return graphQLRepository.fetchUser(stateUsername)\n    }\n\n    private suspend fun fetchUserStory(fetchedUser: User): Story? = storiesRepository.getStories(\n        StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName)\n    )\n\n    private suspend fun fetchUserHighlights(fetchedUser: User): List<Story> = storiesRepository.fetchHighlights(fetchedUser.pk)\n\n    private suspend fun checkAndUpdateFavorite(fetchedUser: User) {\n        try {\n            val favorite = favoriteRepository.getFavorite(fetchedUser.username, FavoriteType.USER)\n            if (favorite == null) {\n                _isFavorite.postValue(false)\n                return\n            }\n            _isFavorite.postValue(true)\n            favoriteRepository.insertOrUpdateFavorite(\n                Favorite(\n                    favorite.id,\n                    fetchedUser.username,\n                    FavoriteType.USER,\n                    fetchedUser.fullName,\n                    fetchedUser.profilePicUrl,\n                    favorite.dateAdded\n                )\n            )\n        } catch (e: Exception) {\n            _isFavorite.postValue(false)\n            Log.e(TAG, \"checkAndUpdateFavorite: \", e)\n        }\n    }\n\n    fun setCurrentUser(currentUser: Resource<User?>) {\n        _currentUser.postValue(currentUser)\n    }\n\n    fun shareDm(result: RankedRecipient) {\n        val mediaId = profile.value?.data?.pk ?: return\n        messageManager?.sendMedia(result, mediaId.toString(10), null, BroadcastItemType.PROFILE, viewModelScope)\n    }\n\n    fun shareDm(recipients: Set<RankedRecipient>) {\n        val mediaId = profile.value?.data?.pk ?: return\n        messageManager?.sendMedia(recipients, mediaId.toString(10), null, BroadcastItemType.PROFILE, viewModelScope)\n    }\n\n    fun refresh() {\n        profileAction.postValue(REFRESH)\n    }\n\n    private val toggleFavoriteControlledRunner = SingleRunner()\n    fun toggleFavorite() {\n        val username = profile.value?.data?.username ?: return\n        val fullName = profile.value?.data?.fullName ?: return\n        val profilePicUrl = profile.value?.data?.profilePicUrl ?: return\n        viewModelScope.launch(Dispatchers.IO) {\n            toggleFavoriteControlledRunner.afterPrevious {\n                try {\n                    val favorite = favoriteRepository.getFavorite(username, FavoriteType.USER)\n                    if (favorite == null) {\n                        // insert\n                        favoriteRepository.insertOrUpdateFavorite(\n                            Favorite(\n                                0,\n                                username,\n                                FavoriteType.USER,\n                                fullName,\n                                profilePicUrl,\n                                LocalDateTime.now()\n                            )\n                        )\n                        _isFavorite.postValue(true)\n                        return@afterPrevious\n                    }\n                    // delete\n                    favoriteRepository.deleteFavorite(username, FavoriteType.USER)\n                    _isFavorite.postValue(false)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"checkAndUpdateFavorite: \", e)\n                }\n            }\n        }\n    }\n\n    private val toggleFollowSingleRunner = SingleRunner()\n    fun toggleFollow(confirmed: Boolean) {\n        viewModelScope.launch(Dispatchers.IO) {\n            toggleFollowSingleRunner.afterPrevious {\n                try {\n                    val following = profile.value?.data?.friendshipStatus?.following ?: false\n                    val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious\n                    val targetUserId = profile.value?.data?.pk ?: return@afterPrevious\n                    val csrfToken = csrfToken ?: return@afterPrevious\n                    val deviceUuid = deviceUuid ?: return@afterPrevious\n                    if (following) {\n                        if (!confirmed) {\n                            _eventLiveData.postValue(Event(ShowConfirmUnfollowDialog))\n                            return@afterPrevious\n                        }\n                        // unfollow\n                        friendshipRepository.unfollow(\n                            csrfToken,\n                            currentUserId,\n                            deviceUuid,\n                            targetUserId\n                        )\n                        profileAction.postValue(REFRESH_FRIENDSHIP)\n                        return@afterPrevious\n                    }\n                    friendshipRepository.follow(\n                        csrfToken,\n                        currentUserId,\n                        deviceUuid,\n                        targetUserId\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"toggleFollow: \", e)\n                }\n            }\n        }\n    }\n\n    private val sendDmSingleRunner = SingleRunner()\n    fun sendDm() {\n        viewModelScope.launch(Dispatchers.IO) {\n            sendDmSingleRunner.afterPrevious {\n                _eventLiveData.postValue(Event(DMButtonState(true)))\n                try {\n                    val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious\n                    val targetUserId = profile.value?.data?.pk ?: return@afterPrevious\n                    val csrfToken = csrfToken ?: return@afterPrevious\n                    val deviceUuid = deviceUuid ?: return@afterPrevious\n                    val username = profile.value?.data?.username ?: return@afterPrevious\n                    val thread = directMessagesRepository.createThread(\n                        csrfToken,\n                        currentUserId,\n                        deviceUuid,\n                        listOf(targetUserId),\n                        null,\n                    )\n                    val inboxManager = DirectMessagesManager.inboxManager\n                    if (!inboxManager.containsThread(thread.threadId)) {\n                        thread.isTemp = true\n                        inboxManager.addThread(thread, 0)\n                    }\n                    val threadId = thread.threadId ?: return@afterPrevious\n                    _eventLiveData.postValue(Event(NavigateToThread(threadId, username)))\n                    delay(200) // Add delay so that the postValue in finally does not overwrite the NavigateToThread event\n                } catch (e: Exception) {\n                    Log.e(TAG, \"sendDm: \", e)\n                } finally {\n                    _eventLiveData.postValue(Event(DMButtonState(false)))\n                }\n            }\n        }\n    }\n\n    private val restrictUserSingleRunner = SingleRunner()\n    fun restrictUser() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            restrictUserSingleRunner.afterPrevious {\n                try {\n                    val profile = profile.value?.data ?: return@afterPrevious\n                    friendshipRepository.toggleRestrict(\n                        csrfToken ?: return@afterPrevious,\n                        deviceUuid ?: return@afterPrevious,\n                        profile.pk,\n                        !(profile.friendshipStatus?.isRestricted ?: false),\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"restrictUser: \", e)\n                }\n            }\n        }\n    }\n\n    private val blockUserSingleRunner = SingleRunner()\n    fun blockUser() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            blockUserSingleRunner.afterPrevious {\n                try {\n                    val profile = profile.value?.data ?: return@afterPrevious\n                    friendshipRepository.changeBlock(\n                        csrfToken ?: return@afterPrevious,\n                        currentUser.value?.data?.pk ?: return@afterPrevious,\n                        deviceUuid ?: return@afterPrevious,\n                        profile.friendshipStatus?.blocking ?: return@afterPrevious,\n                        profile.pk\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"blockUser: \", e)\n                }\n            }\n        }\n    }\n\n    private val muteStoriesSingleRunner = SingleRunner()\n    fun muteStories() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            muteStoriesSingleRunner.afterPrevious {\n                try {\n                    val profile = profile.value?.data ?: return@afterPrevious\n                    friendshipRepository.changeMute(\n                        csrfToken ?: return@afterPrevious,\n                        currentUser.value?.data?.pk ?: return@afterPrevious,\n                        deviceUuid ?: return@afterPrevious,\n                        profile.friendshipStatus?.isMutingReel ?: return@afterPrevious,\n                        profile.pk,\n                        true\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"muteStories: \", e)\n                }\n            }\n        }\n    }\n\n    private val mutePostsSingleRunner = SingleRunner()\n    fun mutePosts() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            mutePostsSingleRunner.afterPrevious {\n                try {\n                    val profile = profile.value?.data ?: return@afterPrevious\n                    friendshipRepository.changeMute(\n                        csrfToken ?: return@afterPrevious,\n                        currentUser.value?.data?.pk ?: return@afterPrevious,\n                        deviceUuid ?: return@afterPrevious,\n                        profile.friendshipStatus?.muting ?: return@afterPrevious,\n                        profile.pk,\n                        false\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"mutePosts: \", e)\n                }\n            }\n        }\n    }\n\n    private val removeFollowerSingleRunner = SingleRunner()\n    fun removeFollower() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            removeFollowerSingleRunner.afterPrevious {\n                try {\n                    friendshipRepository.removeFollower(\n                        csrfToken ?: return@afterPrevious,\n                        currentUser.value?.data?.pk ?: return@afterPrevious,\n                        deviceUuid ?: return@afterPrevious,\n                        profile.value?.data?.pk ?: return@afterPrevious\n                    )\n                    profileAction.postValue(REFRESH_FRIENDSHIP)\n                } catch (e: Exception) {\n                    Log.e(TAG, \"removeFollower: \", e)\n                }\n            }\n        }\n    }\n\n    private val translateBioSingleRunner = SingleRunner()\n    fun translateBio() {\n        if (isLoggedIn.value == false) return\n        viewModelScope.launch(Dispatchers.IO) {\n            translateBioSingleRunner.afterPrevious {\n                try {\n                    val result = mediaRepository.translate(\n                        profile.value?.data?.pk?.toString() ?: return@afterPrevious,\n                        \"3\"\n                    )\n                    if (result.isNullOrBlank()) return@afterPrevious\n                    _eventLiveData.postValue(Event(ShowTranslation(result)))\n                } catch (e: Exception) {\n                    Log.e(TAG, \"translateBio: \", e)\n                }\n            }\n        }\n    }\n\n    /**\n     * Username of profile without '`@`'\n     */\n    val username: LiveData<String> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> \"\"\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.username ?: \"\"\n        }\n    }\n    val profilePicUrl: LiveData<String?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.profilePicUrl\n        }\n    }\n    val fullName: LiveData<String?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> \"\"\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.fullName\n        }\n    }\n    val biography: LiveData<String?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> \"\"\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.biography\n        }\n    }\n    val url: LiveData<String?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> \"\"\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.externalUrl\n        }\n    }\n    val followersCount: LiveData<Long?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.followerCount\n        }\n    }\n    val followingCount: LiveData<Long?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.followingCount\n        }\n    }\n    val postCount: LiveData<Long?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.mediaCount\n        }\n    }\n    val isPrivate: LiveData<Boolean?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.isPrivate\n        }\n    }\n    val isVerified: LiveData<Boolean?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.isVerified\n        }\n    }\n    val friendshipStatus: LiveData<FriendshipStatus?> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.friendshipStatus\n        }\n    }\n    val profileContext: LiveData<Pair<String?, List<UserProfileContextLink>?>> = Transformations.map(profile) {\n        return@map when (it.status) {\n            Resource.Status.ERROR -> null to null\n            Resource.Status.LOADING, Resource.Status.SUCCESS -> it.data?.profileContext to it.data?.profileContextLinksWithUserIds\n        }\n    }\n}\n\n@Suppress(\"UNCHECKED_CAST\")\nclass ProfileFragmentViewModelFactory(\n    private val csrfToken: String?,\n    private val deviceUuid: String?,\n    private val userRepository: UserRepository,\n    private val friendshipRepository: FriendshipRepository,\n    private val storiesRepository: StoriesRepository,\n    private val mediaRepository: MediaRepository,\n    private val graphQLRepository: GraphQLRepository,\n    private val favoriteRepository: FavoriteRepository,\n    private val directMessagesRepository: DirectMessagesRepository,\n    private val messageManager: DirectMessagesManager?,\n    owner: SavedStateRegistryOwner,\n    defaultArgs: Bundle? = null,\n) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {\n    override fun <T : ViewModel> create(\n        key: String,\n        modelClass: Class<T>,\n        handle: SavedStateHandle,\n    ): T {\n        return ProfileFragmentViewModel(\n            handle,\n            csrfToken,\n            deviceUuid,\n            userRepository,\n            friendshipRepository,\n            storiesRepository,\n            mediaRepository,\n            graphQLRepository,\n            favoriteRepository,\n            directMessagesRepository,\n            messageManager,\n            Dispatchers.IO,\n        ) as T\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/SavedCollectionsViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.saved.SavedCollection;\n\npublic class SavedCollectionsViewModel extends ViewModel {\n    private MutableLiveData<List<SavedCollection>> list;\n\n    public MutableLiveData<List<SavedCollection>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/SearchFragmentViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.app.Application\nimport android.util.Log\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Transformations\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.db.datasources.RecentSearchDataSource\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.db.entities.RecentSearch\nimport awais.instagrabber.db.entities.RecentSearch.Companion.fromSearchItem\nimport awais.instagrabber.db.repositories.FavoriteRepository\nimport awais.instagrabber.db.repositories.RecentSearchRepository\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.repositories.responses.search.SearchItem\nimport awais.instagrabber.repositories.responses.search.SearchResponse\nimport awais.instagrabber.utils.*\nimport awais.instagrabber.utils.AppExecutors.mainThread\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.webservices.SearchRepository\nimport com.google.common.collect.ImmutableList\nimport com.google.common.util.concurrent.FutureCallback\nimport com.google.common.util.concurrent.Futures\nimport com.google.common.util.concurrent.SettableFuture\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Dispatchers.IO\nimport kotlinx.coroutines.launch\nimport retrofit2.Call\nimport retrofit2.Callback\nimport retrofit2.Response\nimport java.util.*\nimport java.util.function.BiConsumer\nimport java.util.stream.Collectors\n\nclass SearchFragmentViewModel(application: Application) : AppStateViewModel(application) {\n    private val query = MutableLiveData<String>()\n    private val topResults = MutableLiveData<Resource<List<SearchItem>?>>()\n    private val userResults = MutableLiveData<Resource<List<SearchItem>?>>()\n    private val hashtagResults = MutableLiveData<Resource<List<SearchItem>?>>()\n    private val locationResults = MutableLiveData<Resource<List<SearchItem>?>>()\n    private val searchRepository: SearchRepository by lazy { SearchRepository.getInstance() }\n    private val searchCallback: Debouncer.Callback<String> = object : Debouncer.Callback<String> {\n        override fun call(key: String) {\n            if (tempQuery == null) return\n            query.postValue(tempQuery!!)\n        }\n\n        override fun onError(t: Throwable) {\n            Log.e(TAG, \"onError: \", t)\n        }\n    }\n    private val searchDebouncer = Debouncer(searchCallback, 500)\n    private val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n    private val isLoggedIn = !isEmpty(cookie) && getUserIdFromCookie(cookie) != 0L\n    private val distinctQuery = Transformations.distinctUntilChanged(query)\n    private val recentSearchRepository: RecentSearchRepository by lazy {\n        RecentSearchRepository.getInstance(RecentSearchDataSource.getInstance(application))\n    }\n    private val favoriteRepository: FavoriteRepository by lazy { FavoriteRepository.getInstance(application) }\n    private var tempQuery: String? = null\n    fun getQuery(): LiveData<String> {\n        return distinctQuery\n    }\n\n    fun getTopResults(): LiveData<Resource<List<SearchItem>?>> {\n        return topResults\n    }\n\n    fun getUserResults(): LiveData<Resource<List<SearchItem>?>> {\n        return userResults\n    }\n\n    fun getHashtagResults(): LiveData<Resource<List<SearchItem>?>> {\n        return hashtagResults\n    }\n\n    fun getLocationResults(): LiveData<Resource<List<SearchItem>?>> {\n        return locationResults\n    }\n\n    fun submitQuery(query: String?) {\n        var localQuery = query\n        if (query == null) {\n            localQuery = \"\"\n        }\n        if (tempQuery != null && localQuery!!.lowercase(Locale.getDefault()) == tempQuery!!.lowercase(Locale.getDefault())) return\n        tempQuery = query\n        if (isEmpty(query)) {\n            // If empty immediately post it\n            searchDebouncer.cancel(QUERY)\n            this.query.postValue(\"\")\n            return\n        }\n        searchDebouncer.call(QUERY)\n    }\n\n    fun search(\n        query: String,\n        type: FavoriteType\n    ) {\n        val liveData = getLiveDataByType(type) ?: return\n        if (isEmpty(query)) {\n            showRecentSearchesAndFavorites(type, liveData)\n            return\n        }\n        if (query == \"@\" || query == \"#\") return\n        val c: String\n        c = when (type) {\n            FavoriteType.TOP -> \"blended\"\n            FavoriteType.USER -> \"user\"\n            FavoriteType.HASHTAG -> \"hashtag\"\n            FavoriteType.LOCATION -> \"place\"\n            else -> return\n        }\n        liveData.postValue(loading<List<SearchItem>?>(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val response = searchRepository.search(isLoggedIn, query, c)\n                parseResponse(response, type)\n            }\n            catch (e: Exception) {\n                sendErrorResponse(type)\n            }\n        }\n    }\n\n    private fun showRecentSearchesAndFavorites(\n        type: FavoriteType,\n        liveData: MutableLiveData<Resource<List<SearchItem>?>>\n    ) {\n        val recentResultsFuture = SettableFuture.create<List<RecentSearch>>()\n        val favoritesFuture = SettableFuture.create<List<Favorite>>()\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val recentSearches = recentSearchRepository.getAllRecentSearches()\n                recentResultsFuture.set(\n                    if (type == FavoriteType.TOP) recentSearches\n                    else recentSearches.stream()\n                        .filter { (_, _, _, _, _, type1) -> type1 === type }\n                        .collect(Collectors.toList())\n                )\n            }\n            catch (e: Exception) {\n                recentResultsFuture.set(emptyList())\n            }\n            try {\n                val favorites = favoriteRepository.getAllFavorites()\n                favoritesFuture.set(\n                    if (type == FavoriteType.TOP) favorites\n                    else favorites\n                        .stream()\n                        .filter { (_, _, type1) -> type1 === type }\n                        .collect(Collectors.toList())\n                )\n            }\n            catch (e: Exception) {\n                favoritesFuture.set(emptyList())\n            }\n        }\n        val listenableFuture = Futures.allAsList<List<*>>(recentResultsFuture, favoritesFuture)\n        Futures.addCallback(listenableFuture, object : FutureCallback<List<List<*>?>?> {\n            override fun onSuccess(result: List<List<*>?>?) {\n                if (!isEmpty(tempQuery)) return  // Make sure user has not entered anything before updating results\n                if (result == null) {\n                    liveData.postValue(success(emptyList()))\n                    return\n                }\n                try {\n                    liveData.postValue(\n                        success(\n                            ImmutableList.builder<SearchItem>()\n                                .addAll(SearchItem.fromRecentSearch(result[0] as List<RecentSearch?>?))\n                                .addAll(SearchItem.fromFavorite(result[1] as List<Favorite?>?))\n                                .build()\n                        )\n                    )\n                } catch (e: Exception) {\n                    Log.e(TAG, \"onSuccess: \", e)\n                    liveData.postValue(success(emptyList()))\n                }\n            }\n\n            override fun onFailure(t: Throwable) {\n                if (!isEmpty(tempQuery)) return\n                liveData.postValue(success(emptyList()))\n                Log.e(TAG, \"onFailure: \", t)\n            }\n        }, mainThread)\n    }\n\n    private fun sendErrorResponse(type: FavoriteType) {\n        val liveData = getLiveDataByType(type) ?: return\n        liveData.postValue(error(null, emptyList()))\n    }\n\n    private fun getLiveDataByType(type: FavoriteType): MutableLiveData<Resource<List<SearchItem>?>>? {\n        val liveData: MutableLiveData<Resource<List<SearchItem>?>>\n        liveData = when (type) {\n            FavoriteType.TOP -> topResults\n            FavoriteType.USER -> userResults\n            FavoriteType.HASHTAG -> hashtagResults\n            FavoriteType.LOCATION -> locationResults\n            else -> return null\n        }\n        return liveData\n    }\n\n    private fun parseResponse(\n        body: SearchResponse,\n        type: FavoriteType\n    ) {\n        val liveData = getLiveDataByType(type) ?: return\n        if (isLoggedIn) {\n            if (body.list == null) {\n                liveData.postValue(success(emptyList()))\n                return\n            }\n            if (type === FavoriteType.HASHTAG || type === FavoriteType.LOCATION) {\n                liveData.postValue(success(body.list\n                    .stream()\n                    .filter { i: SearchItem -> i.user == null }\n                    .collect(Collectors.toList())))\n                return\n            }\n            liveData.postValue(success(body.list))\n            return\n        }\n\n        // anonymous\n        val list: List<SearchItem>?\n        list = when (type) {\n            FavoriteType.TOP -> ImmutableList\n                .builder<SearchItem>()\n                .addAll(body.users ?: emptyList())\n                .addAll(body.hashtags ?: emptyList())\n                .addAll(body.places ?: emptyList())\n                .build()\n            FavoriteType.USER -> body.users\n            FavoriteType.HASHTAG -> body.hashtags\n            FavoriteType.LOCATION -> body.places\n            else -> return\n        }\n        liveData.postValue(success(list))\n    }\n\n    fun saveToRecentSearches(searchItem: SearchItem?) {\n        if (searchItem == null) return\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val recentSearch = fromSearchItem(searchItem)\n                recentSearchRepository.insertOrUpdateRecentSearch(recentSearch!!)\n            } catch (e: Exception) {\n                Log.e(TAG, \"saveToRecentSearches: \", e)\n            }\n        }\n    }\n\n    fun deleteRecentSearch(searchItem: SearchItem?): LiveData<Resource<Any?>>? {\n        if (searchItem == null || !searchItem.isRecent) return null\n        val (_, igId, _, _, _, type) = fromSearchItem(searchItem) ?: return null\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                recentSearchRepository.deleteRecentSearchByIgIdAndType(igId, type)\n                data.postValue(success(Any()))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    companion object {\n        private val TAG = SearchFragmentViewModel::class.java.simpleName\n        private const val QUERY = \"query\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport android.net.Uri\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport awais.instagrabber.R\nimport awais.instagrabber.managers.DirectMessagesManager\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.Resource.Companion.error\nimport awais.instagrabber.models.Resource.Companion.loading\nimport awais.instagrabber.models.Resource.Companion.success\nimport awais.instagrabber.models.enums.BroadcastItemType\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.models.enums.StoryPaginationType\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient\nimport awais.instagrabber.repositories.responses.stories.*\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.getCsrfTokenFromCookie\nimport awais.instagrabber.utils.getUserIdFromCookie\nimport awais.instagrabber.webservices.MediaRepository\nimport awais.instagrabber.webservices.StoriesRepository\nimport com.google.common.collect.ImmutableList\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass StoryFragmentViewModel : ViewModel() {\n    // large data\n    private val currentStory = MutableLiveData<Story?>()\n    private val currentMedia = MutableLiveData<StoryMedia>()\n\n    // small data\n    private val storyTitle = MutableLiveData<String>()\n    private val date = MutableLiveData<String>()\n    private val type = MutableLiveData<MediaItemType>()\n    private val poll = MutableLiveData<PollSticker>()\n    private val quiz = MutableLiveData<QuizSticker>()\n    private val question = MutableLiveData<QuestionSticker>()\n    private val slider = MutableLiveData<SliderSticker>()\n    private val swipeUp = MutableLiveData<String>()\n    private val linkedPost = MutableLiveData<String>()\n    private val appAttribution = MutableLiveData<StoryAppAttribution>()\n    private val reelMentions = MutableLiveData<List<Triple<String, String?, FavoriteType>>>()\n\n    // process\n    private val currentIndex = MutableLiveData<Int>()\n    private val pagination = MutableLiveData(StoryPaginationType.DO_NOTHING)\n    private val options = MutableLiveData<Triple<List<Pair<Int, Int>>, String?, String?>>()\n    private val seen = MutableLiveData<Triple<String, Long, Long>>()\n\n    // utils\n    private var messageManager: DirectMessagesManager? = null\n    private val cookie = Utils.settingsHelper.getString(Constants.COOKIE)\n    private val deviceId = Utils.settingsHelper.getString(Constants.DEVICE_UUID)\n    private val csrfToken = getCsrfTokenFromCookie(cookie)\n    private val userId = getUserIdFromCookie(cookie)\n    private val storiesRepository: StoriesRepository by lazy { StoriesRepository.getInstance() }\n    private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }\n\n    // for highlights ONLY\n    val highlights = MutableLiveData<List<Story>?>()\n\n    /* set functions */\n\n    fun setStory(story: Story) {\n        currentStory.postValue(story)\n        storyTitle.postValue(story.title ?: story.user?.username)\n        if (story.broadcast != null) {\n            date.postValue(story.dateTime)\n            type.postValue(MediaItemType.MEDIA_TYPE_LIVE)\n            pagination.postValue(StoryPaginationType.DO_NOTHING)\n            return\n        }\n        if (story.items == null || story.items.size == 0) {\n            pagination.postValue(StoryPaginationType.ERROR)\n            return\n        }\n    }\n\n    fun setMedia(index: Int) {\n        if (currentStory.value?.items == null) return\n        if (index < 0 || index >= currentStory.value!!.items!!.size) {\n            pagination.postValue(if (index < 0) StoryPaginationType.BACKWARD else StoryPaginationType.FORWARD)\n            return\n        }\n        currentIndex.postValue(index)\n        val story: Story? = currentStory.value\n        val media = story!!.items!!.get(index)\n        currentMedia.postValue(media)\n        date.postValue(media.date)\n        type.postValue(media.type)\n        initStickers(media)\n    }\n\n    fun setSingleMedia(media: StoryMedia) {\n        currentStory.postValue(null)\n        currentIndex.postValue(0)\n        currentMedia.postValue(media)\n        date.postValue(media.date)\n        type.postValue(media.type)\n    }\n\n    private fun initStickers(media: StoryMedia) {\n        val builder = ImmutableList.builder<Pair<Int, Int>>()\n        var linkedText: String? = null\n        var appText: String? = null\n        if (setMentions(media)) builder.add(Pair(R.id.mentions, R.string.story_mentions))\n        if (setQuiz(media)) builder.add(Pair(R.id.quiz, R.string.story_quiz))\n        if (setQuestion(media)) builder.add(Pair(R.id.question, R.string.story_question))\n        if (setPoll(media)) builder.add(Pair(R.id.poll, R.string.story_poll))\n        if (setSlider(media)) builder.add(Pair(R.id.slider, R.string.story_slider))\n        if (setLinkedPost(media)) builder.add(Pair(R.id.viewStoryPost, R.string.view_post))\n        if (setStoryCta(media)) {\n            linkedText = media.linkText\n            builder.add(Pair(R.id.swipeUp, 0))\n        }\n        if (setStoryAppAttribution(media)) {\n            appText = media.storyAppAttribution!!.appActionText\n            builder.add(Pair(R.id.spotify, 0))\n        }\n        options.postValue(Triple(builder.build(), linkedText, appText))\n    }\n\n    private fun setMentions(media: StoryMedia): Boolean {\n        val mentions: MutableList<Triple<String, String?, FavoriteType>> = mutableListOf()\n        if (media.reelMentions != null)\n            mentions.addAll(media.reelMentions.map{\n                Triple(\"@\" + it.user?.username, it.user?.username, FavoriteType.USER)\n            })\n        if (media.storyHashtags != null)\n            mentions.addAll(media.storyHashtags.map{\n                Triple(\"#\" + it.hashtag?.name, it.hashtag?.name, FavoriteType.HASHTAG)\n            })\n        if (media.storyLocations != null)\n            mentions.addAll(media.storyLocations.map{\n                Triple(it.location?.name ?: \"\", it.location?.pk?.toString(10), FavoriteType.LOCATION)\n            })\n        reelMentions.postValue(mentions.filterNot { it.second.isNullOrEmpty() } .distinct())\n        return !mentions.isEmpty()\n    }\n\n    private fun setPoll(media: StoryMedia): Boolean {\n        poll.postValue(media.storyPolls?.get(0)?.pollSticker ?: return false)\n        return true\n    }\n\n    private fun setQuiz(media: StoryMedia): Boolean {\n        quiz.postValue(media.storyQuizs?.get(0)?.quizSticker ?: return false)\n        return true\n    }\n\n    private fun setQuestion(media: StoryMedia): Boolean {\n        val questionSticker = media.storyQuestions?.get(0)?.questionSticker ?: return false\n        if (questionSticker.questionType.equals(\"music\")) return false\n        question.postValue(questionSticker)\n        return true\n    }\n\n    private fun setSlider(media: StoryMedia): Boolean {\n        slider.postValue(media.storySliders?.get(0)?.sliderSticker ?: return false)\n        return true\n    }\n\n    private fun setLinkedPost(media: StoryMedia): Boolean {\n        linkedPost.postValue(media.storyFeedMedia?.get(0)?.mediaId ?: return false)\n        return true\n    }\n\n    private fun setStoryCta(media: StoryMedia): Boolean {\n        val webUri = media.storyCta?.get(0)?.links?.get(0)?.webUri ?: return false\n        val parsedUri = Uri.parse(webUri)\n        val cleanUri = if (parsedUri.host.equals(\"l.instagram.com\")) parsedUri.getQueryParameter(\"u\")\n                       else null\n        swipeUp.postValue(if (cleanUri != null && Uri.parse(cleanUri).scheme?.startsWith(\"http\") == true) cleanUri\n                          else webUri)\n        return true\n    }\n\n    private fun setStoryAppAttribution(media: StoryMedia): Boolean {\n        appAttribution.postValue(media.storyAppAttribution ?: return false)\n        return true\n    }\n\n    /* get functions */\n\n    fun getCurrentStory(): LiveData<Story?> {\n        return currentStory\n    }\n\n    fun getCurrentIndex(): LiveData<Int> {\n        return currentIndex\n    }\n\n    fun getCurrentMedia(): LiveData<StoryMedia> {\n        return currentMedia\n    }\n\n    fun getPagination(): LiveData<StoryPaginationType> {\n        return pagination\n    }\n\n    fun getDate(): LiveData<String> {\n        return date\n    }\n\n    fun getTitle(): LiveData<String> {\n        return storyTitle\n    }\n\n    fun getType(): LiveData<MediaItemType> {\n        return type\n    }\n\n    fun getMedia(): LiveData<StoryMedia> {\n        return currentMedia\n    }\n\n    fun getMention(index: Int): Triple<String, String?, FavoriteType>? {\n        return reelMentions.value?.get(index)\n    }\n\n    fun getMentionTexts(): Array<String> {\n        return reelMentions.value!!.map { it.first } .toTypedArray()\n    }\n\n    fun getPoll(): LiveData<PollSticker> {\n        return poll\n    }\n\n    fun getQuestion(): LiveData<QuestionSticker> {\n        return question\n    }\n\n    fun getQuiz(): LiveData<QuizSticker> {\n        return quiz\n    }\n\n    fun getSlider(): LiveData<SliderSticker> {\n        return slider\n    }\n\n    fun getLinkedPost(): LiveData<Resource<Media?>> {\n        val data = MutableLiveData<Resource<Media?>>()\n        data.postValue(loading(null))\n        val postId = linkedPost.value\n        if (postId == null) data.postValue(error(\"No post ID supplied\", null))\n        else viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val media = mediaRepository.fetch(postId.toLong())\n                data.postValue(success(media))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun getSwipeUp(): String? {\n        return swipeUp.value\n    }\n\n    fun getAppAttribution(): String? {\n        return appAttribution.value?.url\n    }\n\n    fun getOptions(): LiveData<Triple<List<Pair<Int, Int>>, String?, String?>> {\n        return options\n    }\n\n    /* action functions */\n\n    fun answerPoll(w: Int): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val oldPoll: PollSticker = poll.value!!\n                val response = storiesRepository.respondToPoll(\n                    csrfToken!!,\n                    userId,\n                    deviceId,\n                    currentMedia.value!!.pk,\n                    oldPoll.pollId,\n                    w\n                )\n                if (!\"ok\".equals(response.status))\n                    throw Exception(\"Instagram returned status \\\"\" + response.status + \"\\\"\")\n                val tally = oldPoll.tallies.get(w)\n                val newTally = tally.copy(count = tally.count + 1)\n                val newTallies = oldPoll.tallies.toMutableList()\n                newTallies.set(w, newTally)\n                poll.postValue(oldPoll.copy(viewerVote = w, tallies = newTallies.toList()))\n                data.postValue(success(null))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun answerQuiz(w: Int): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val oldQuiz = quiz.value!!\n                val response = storiesRepository.respondToQuiz(\n                    csrfToken!!,\n                    userId,\n                    deviceId,\n                    currentMedia.value!!.pk,\n                    oldQuiz.quizId,\n                    w\n                )\n                if (!\"ok\".equals(response.status))\n                    throw Exception(\"Instagram returned status \\\"\" + response.status + \"\\\"\")\n                val tally = oldQuiz.tallies.get(w)\n                val newTally = tally.copy(count = tally.count + 1)\n                val newTallies = oldQuiz.tallies.toMutableList()\n                newTallies.set(w, newTally)\n                quiz.postValue(oldQuiz.copy(viewerAnswer = w, tallies = newTallies.toList()))\n                data.postValue(success(null))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun answerQuestion(a: String): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val response = storiesRepository.respondToQuestion(\n                    csrfToken!!,\n                    userId,\n                    deviceId,\n                    currentMedia.value!!.pk,\n                    question.value!!.questionId,\n                    a\n                )\n                if (!\"ok\".equals(response.status))\n                    throw Exception(\"Instagram returned status \\\"\" + response.status + \"\\\"\")\n                data.postValue(success(null))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun answerSlider(a: Double): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val oldSlider = slider.value!!\n                val response = storiesRepository.respondToSlider(\n                    csrfToken!!,\n                    userId,\n                    deviceId,\n                    currentMedia.value!!.pk,\n                    oldSlider.sliderId,\n                    a\n                )\n                if (!\"ok\".equals(response.status))\n                    throw Exception(\"Instagram returned status \\\"\" + response.status + \"\\\"\")\n                val newVoteCount = (oldSlider.sliderVoteCount ?: 0) + 1\n                val newAverage = if (oldSlider.sliderVoteAverage == null) a\n                                 else (oldSlider.sliderVoteAverage * oldSlider.sliderVoteCount!! + a) / newVoteCount\n                slider.postValue(oldSlider.copy(viewerCanVote = false,\n                                                sliderVoteCount = newVoteCount,\n                                                viewerVote = a,\n                                                sliderVoteAverage = newAverage))\n                data.postValue(success(null))\n            }\n            catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun reply(a: String): LiveData<Resource<Any?>>? {\n        if (messageManager == null) {\n            messageManager = DirectMessagesManager\n        }\n        return messageManager?.replyToStory(\n            currentStory.value?.user?.pk,\n            currentStory.value?.id,\n            currentMedia.value?.id,\n            a,\n            viewModelScope\n        )\n    }\n\n    fun shareDm(result: RankedRecipient) {\n        if (messageManager == null) {\n            messageManager = DirectMessagesManager\n        }\n        val mediaId = currentMedia.value?.id ?: return\n        val reelId = currentStory.value?.id ?: return\n        messageManager?.sendMedia(result, mediaId, reelId, BroadcastItemType.STORY, viewModelScope)\n    }\n\n    fun shareDm(recipients: Set<RankedRecipient>) {\n        if (messageManager == null) {\n            messageManager = DirectMessagesManager\n        }\n        val mediaId = currentMedia.value?.id ?: return\n        val reelId = currentStory.value?.id ?: return\n        messageManager?.sendMedia(recipients, mediaId, reelId, BroadcastItemType.STORY, viewModelScope)\n    }\n\n    fun paginate(backward: Boolean) {\n        var index = currentIndex.value!!\n        index = if (backward) index - 1 else index + 1\n        if (index < 0 || index >= currentStory.value!!.items!!.size) skip(backward)\n        setMedia(index)\n    }\n\n    fun skip(backward: Boolean) {\n        pagination.postValue(if (backward) StoryPaginationType.BACKWARD else StoryPaginationType.FORWARD)\n    }\n\n    fun fetchStory(fetchOptions: StoryViewerOptions?): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val story = storiesRepository.getStories(fetchOptions!!)\n                setStory(story!!)\n                data.postValue(success(null))\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun fetchHighlights(id: Long) {\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val result = storiesRepository.fetchHighlights(id)\n                highlights.postValue(result)\n            } catch (e: Exception) {\n            }\n        }\n    }\n\n    fun fetchSingleMedia(mediaId: Long): LiveData<Resource<Any?>> {\n        val data = MutableLiveData<Resource<Any?>>()\n        data.postValue(loading(null))\n        viewModelScope.launch(Dispatchers.IO) {\n            try {\n                val storyMedia = storiesRepository.fetch(mediaId)\n                setSingleMedia(storyMedia!!)\n                data.postValue(success(null))\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n\n    fun markAsSeen(storyMedia: StoryMedia): LiveData<Resource<Story?>> {\n        val data = MutableLiveData<Resource<Story?>>()\n        data.postValue(loading(null))\n        val oldStory = currentStory.value!!\n        if (oldStory.seen != null && oldStory.seen >= storyMedia.takenAt) data.postValue(success(null))\n        else viewModelScope.launch(Dispatchers.IO) {\n            try {\n                storiesRepository.seen(\n                    csrfToken!!,\n                    userId,\n                    deviceId,\n                    storyMedia.id,\n                    storyMedia.takenAt,\n                    System.currentTimeMillis() / 1000\n                )\n                val newStory = oldStory.copy(seen = storyMedia.takenAt)\n                data.postValue(success(newStory))\n            } catch (e: Exception) {\n                data.postValue(error(e.message, null))\n            }\n        }\n        return data\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/TopicClusterViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.discover.TopicCluster;\n\npublic class TopicClusterViewModel extends ViewModel {\n    private MutableLiveData<List<TopicCluster>> list;\n\n    public MutableLiveData<List<TopicCluster>> getList() {\n        if (list == null) {\n            list = new MutableLiveData<>();\n        }\n        return list;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java",
    "content": "package awais.instagrabber.viewmodels;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MutableLiveData;\nimport androidx.lifecycle.ViewModel;\n\nimport com.google.common.collect.ImmutableList;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.fragments.UserSearchMode;\nimport awais.instagrabber.models.Resource;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.directmessages.RankedRecipient;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.CookieUtils;\nimport awais.instagrabber.utils.CoroutineUtilsKt;\nimport awais.instagrabber.utils.Debouncer;\nimport awais.instagrabber.utils.RankedRecipientsCache;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.webservices.DirectMessagesRepository;\nimport awais.instagrabber.webservices.UserRepository;\nimport kotlinx.coroutines.Dispatchers;\nimport okhttp3.ResponseBody;\nimport retrofit2.Call;\nimport retrofit2.Response;\n\nimport static awais.instagrabber.utils.Utils.settingsHelper;\n\npublic class UserSearchViewModel extends ViewModel {\n    private static final String TAG = UserSearchViewModel.class.getSimpleName();\n    public static final String DEBOUNCE_KEY = \"search\";\n\n    private String prevQuery;\n    private String currentQuery;\n    private Call<?> searchRequest;\n    private long[] hideUserIds;\n    private String[] hideThreadIds;\n    private UserSearchMode searchMode;\n    private boolean showGroups;\n    private boolean waitingForCache;\n    private boolean showCachedResults;\n\n    private final MutableLiveData<Resource<List<RankedRecipient>>> recipients = new MutableLiveData<>();\n    private final MutableLiveData<Boolean> showAction = new MutableLiveData<>(false);\n    private final Debouncer<String> searchDebouncer;\n    private final Set<RankedRecipient> selectedRecipients = new HashSet<>();\n    private final UserRepository userRepository;\n    private final DirectMessagesRepository directMessagesRepository;\n    private final RankedRecipientsCache rankedRecipientsCache;\n\n    public UserSearchViewModel() {\n        final String cookie = settingsHelper.getString(Constants.COOKIE);\n        final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);\n        final long viewerId = CookieUtils.getUserIdFromCookie(cookie);\n        final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);\n        if (TextUtils.isEmpty(csrfToken) || viewerId <= 0 || TextUtils.isEmpty(deviceUuid)) {\n            throw new IllegalArgumentException(\"User is not logged in!\");\n        }\n        userRepository = UserRepository.Companion.getInstance();\n        directMessagesRepository = DirectMessagesRepository.Companion.getInstance();\n        rankedRecipientsCache = RankedRecipientsCache.INSTANCE;\n        if ((rankedRecipientsCache.isFailed() || rankedRecipientsCache.isExpired()) && !rankedRecipientsCache.isUpdateInitiated()) {\n            updateRankedRecipientCache();\n        }\n        final Debouncer.Callback<String> searchCallback = new Debouncer.Callback<String>() {\n            @Override\n            public void call(final String key) {\n                if (currentQuery != null && currentQuery.equalsIgnoreCase(prevQuery)) return;\n                sendSearchRequest();\n                prevQuery = currentQuery;\n            }\n\n            @Override\n            public void onError(final Throwable t) {\n                Log.e(TAG, \"onError: \", t);\n            }\n        };\n        searchDebouncer = new Debouncer<>(searchCallback, 1000);\n    }\n\n    private void updateRankedRecipientCache() {\n        rankedRecipientsCache.setUpdateInitiated(true);\n        directMessagesRepository.rankedRecipients(\n                null,\n                null,\n                null,\n                CoroutineUtilsKt.getContinuation((response, throwable) -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"updateRankedRecipientCache: \", throwable);\n                        rankedRecipientsCache.setUpdateInitiated(false);\n                        rankedRecipientsCache.setFailed(true);\n                        continueSearchIfRequired();\n                        return;\n                    }\n                    rankedRecipientsCache.setResponse(response);\n                    rankedRecipientsCache.setUpdateInitiated(false);\n                    continueSearchIfRequired();\n                }, Dispatchers.getIO())\n        );\n    }\n\n    private void continueSearchIfRequired() {\n        if (!waitingForCache) {\n            if (showCachedResults) {\n                recipients.postValue(Resource.success(getCachedRecipients()));\n            }\n            return;\n        }\n        waitingForCache = false;\n        sendSearchRequest();\n    }\n\n    public LiveData<Resource<List<RankedRecipient>>> getRecipients() {\n        return recipients;\n    }\n\n    public void search(@Nullable final String query) {\n        currentQuery = query;\n        if (TextUtils.isEmpty(query)) {\n            cancelSearch();\n            if (showCachedResults) {\n                recipients.postValue(Resource.success(getCachedRecipients()));\n            }\n            return;\n        }\n        recipients.postValue(Resource.loading(getCachedRecipients()));\n        searchDebouncer.call(DEBOUNCE_KEY);\n    }\n\n    private void sendSearchRequest() {\n        if (!rankedRecipientsCache.isFailed()) { // to avoid infinite loop in case of any network issues\n            if (rankedRecipientsCache.isUpdateInitiated()) {\n                // wait for cache first\n                waitingForCache = true;\n                return;\n            }\n            if (rankedRecipientsCache.isExpired()) {\n                // update cache first\n                updateRankedRecipientCache();\n                waitingForCache = true;\n                return;\n            }\n        }\n        switch (searchMode) {\n            case RAVEN:\n            case RESHARE:\n                rankedRecipientSearch();\n                break;\n            case USER_SEARCH:\n            default:\n                defaultUserSearch();\n                break;\n        }\n    }\n\n    private void defaultUserSearch() {\n        userRepository.search(currentQuery, CoroutineUtilsKt.getContinuation((userSearchResponse, throwable) -> {\n            if (throwable != null) {\n                Log.e(TAG, \"onFailure: \", throwable);\n                recipients.postValue(Resource.error(throwable.getMessage(), getCachedRecipients()));\n                searchRequest = null;\n                return;\n            }\n            if (userSearchResponse == null) {\n                recipients.postValue(Resource.error(R.string.generic_null_response, getCachedRecipients()));\n                searchRequest = null;\n                return;\n            }\n            final List<RankedRecipient> list = userSearchResponse\n                    .getUsers()\n                    .stream()\n                    .map(RankedRecipient::of)\n                    .collect(Collectors.toList());\n            recipients.postValue(Resource.success(mergeResponseWithCache(list)));\n            searchRequest = null;\n        }));\n    }\n\n    private void rankedRecipientSearch() {\n        directMessagesRepository.rankedRecipients(\n                searchMode.getMode(),\n                showGroups,\n                currentQuery,\n                CoroutineUtilsKt.getContinuation((response, throwable) -> {\n                    if (throwable != null) {\n                        Log.e(TAG, \"rankedRecipientSearch: \", throwable);\n                        recipients.postValue(Resource.error(throwable.getMessage(), getCachedRecipients()));\n                        return;\n                    }\n                    final List<RankedRecipient> list = response.getRankedRecipients();\n                    if (list != null) {\n                        recipients.postValue(Resource.success(mergeResponseWithCache(list)));\n                    }\n                }, Dispatchers.getIO())\n        );\n    }\n\n    private List<RankedRecipient> mergeResponseWithCache(@NonNull final List<RankedRecipient> list) {\n        final Iterator<RankedRecipient> iterator = list.stream()\n                                                       .filter(Objects::nonNull)\n                                                       .filter(this::filterValidRecipients)\n                                                       .filter(this::filterOutGroups)\n                                                       .filter(this::filterIdsToHide)\n                                                       .iterator();\n        return ImmutableList.<RankedRecipient>builder()\n                .addAll(getCachedRecipients()) // add cached results first\n                .addAll(iterator)\n                .build();\n    }\n\n    @NonNull\n    private List<RankedRecipient> getCachedRecipients() {\n        final List<RankedRecipient> rankedRecipients = rankedRecipientsCache.getRankedRecipients();\n        final List<RankedRecipient> list = rankedRecipients != null ? rankedRecipients : Collections.emptyList();\n        return list.stream()\n                   .filter(Objects::nonNull)\n                   .filter(this::filterValidRecipients)\n                   .filter(this::filterOutGroups)\n                   .filter(this::filterQuery)\n                   .filter(this::filterIdsToHide)\n                   .collect(Collectors.toList());\n    }\n\n    private void handleErrorResponse(final Response<?> response, boolean updateResource) {\n        final ResponseBody errorBody = response.errorBody();\n        if (errorBody == null) {\n            if (updateResource) {\n                recipients.postValue(Resource.error(R.string.generic_failed_request, getCachedRecipients()));\n            }\n            return;\n        }\n        String errorString;\n        try {\n            errorString = errorBody.string();\n            Log.e(TAG, \"handleErrorResponse: \" + errorString);\n        } catch (IOException e) {\n            Log.e(TAG, \"handleErrorResponse: \", e);\n            errorString = e.getMessage();\n        }\n        if (updateResource) {\n            recipients.postValue(Resource.error(errorString, getCachedRecipients()));\n        }\n    }\n\n    public void cleanup() {\n        searchDebouncer.terminate();\n    }\n\n    public void setSelectedRecipient(final RankedRecipient recipient, final boolean selected) {\n        if (selected) {\n            selectedRecipients.add(recipient);\n        } else {\n            selectedRecipients.remove(recipient);\n        }\n        showAction.postValue(!selectedRecipients.isEmpty());\n    }\n\n    public Set<RankedRecipient> getSelectedRecipients() {\n        return selectedRecipients;\n    }\n\n    public void clearResults() {\n        recipients.postValue(Resource.success(Collections.emptyList()));\n        prevQuery = \"\";\n    }\n\n    public void cancelSearch() {\n        searchDebouncer.cancel(DEBOUNCE_KEY);\n        if (searchRequest != null) {\n            searchRequest.cancel();\n            searchRequest = null;\n        }\n    }\n\n    public LiveData<Boolean> showAction() {\n        return showAction;\n    }\n\n    public void setSearchMode(final UserSearchMode searchMode) {\n        this.searchMode = searchMode;\n    }\n\n    public void setShowGroups(final boolean showGroups) {\n        this.showGroups = showGroups;\n    }\n\n    public void setHideUserIds(final long[] hideUserIds) {\n        if (hideUserIds != null) {\n            final long[] copy = Arrays.copyOf(hideUserIds, hideUserIds.length);\n            Arrays.sort(copy);\n            this.hideUserIds = copy;\n            return;\n        }\n        this.hideUserIds = null;\n    }\n\n    public void setHideThreadIds(final String[] hideThreadIds) {\n        if (hideThreadIds != null) {\n            final String[] copy = Arrays.copyOf(hideThreadIds, hideThreadIds.length);\n            Arrays.sort(copy);\n            this.hideThreadIds = copy;\n            return;\n        }\n        this.hideThreadIds = null;\n    }\n\n    private boolean filterOutGroups(@NonNull RankedRecipient recipient) {\n        // if showGroups is false, remove groups from the list\n        if (showGroups || recipient.getThread() == null) {\n            return true;\n        }\n        return !recipient.getThread().isGroup();\n    }\n\n    private boolean filterValidRecipients(@NonNull RankedRecipient recipient) {\n        // check if both user and thread are null\n        return recipient.getUser() != null || recipient.getThread() != null;\n    }\n\n    private boolean filterIdsToHide(@NonNull RankedRecipient recipient) {\n        if (hideThreadIds != null && recipient.getThread() != null) {\n            return Arrays.binarySearch(hideThreadIds, recipient.getThread().getThreadId()) < 0;\n        }\n        if (hideUserIds != null) {\n            long pk = -1;\n            if (recipient.getUser() != null) {\n                pk = recipient.getUser().getPk();\n            } else if (recipient.getThread() != null && !recipient.getThread().isGroup()) {\n                final User user = recipient.getThread().getUsers().get(0);\n                pk = user.getPk();\n            }\n            return Arrays.binarySearch(hideUserIds, pk) < 0;\n        }\n        return true;\n    }\n\n    private boolean filterQuery(@NonNull RankedRecipient recipient) {\n        if (TextUtils.isEmpty(currentQuery)) {\n            return true;\n        }\n        if (recipient.getThread() != null) {\n            return recipient.getThread().getThreadTitle().toLowerCase().contains(currentQuery.toLowerCase());\n        }\n        return recipient.getUser().getUsername().toLowerCase().contains(currentQuery.toLowerCase())\n                || recipient.getUser().getFullName().toLowerCase().contains(currentQuery.toLowerCase());\n    }\n\n    public void showCachedResults() {\n        this.showCachedResults = true;\n        if (rankedRecipientsCache.isUpdateInitiated()) return;\n        recipients.postValue(Resource.success(getCachedRecipients()));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/factories/DirectSettingsViewModelFactory.java",
    "content": "package awais.instagrabber.viewmodels.factories;\n\nimport android.app.Application;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.ViewModel;\nimport androidx.lifecycle.ViewModelProvider;\n\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.viewmodels.DirectSettingsViewModel;\n\npublic class DirectSettingsViewModelFactory implements ViewModelProvider.Factory {\n\n    private final Application application;\n    private final String threadId;\n    private final boolean pending;\n    private final User currentUser;\n\n    public DirectSettingsViewModelFactory(@NonNull final Application application,\n                                          @NonNull final String threadId,\n                                          final boolean pending,\n                                          @NonNull final User currentUser) {\n        this.application = application;\n        this.threadId = threadId;\n        this.pending = pending;\n        this.currentUser = currentUser;\n    }\n\n    @NonNull\n    @Override\n    public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {\n        //noinspection unchecked\n        return (T) new DirectSettingsViewModel(application, threadId, pending, currentUser);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/viewmodels/factories/DirectThreadViewModelFactory.java",
    "content": "package awais.instagrabber.viewmodels.factories;\n\nimport android.app.Application;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.ViewModel;\nimport androidx.lifecycle.ViewModelProvider;\n\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.viewmodels.DirectThreadViewModel;\n\npublic class DirectThreadViewModelFactory implements ViewModelProvider.Factory {\n\n    private final Application application;\n    private final String threadId;\n    private final boolean pending;\n    private final User currentUser;\n\n    public DirectThreadViewModelFactory(@NonNull final Application application,\n                                        @NonNull final String threadId,\n                                        final boolean pending,\n                                        @NonNull final User currentUser) {\n        this.application = application;\n        this.threadId = threadId;\n        this.pending = pending;\n        this.currentUser = currentUser;\n    }\n\n    @NonNull\n    @Override\n    public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {\n        //noinspection unchecked\n        return (T) new DirectThreadViewModel(application, threadId, pending, currentUser);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/CollectionService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport android.text.TextUtils;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.repositories.CollectionRepository;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.utils.Utils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class CollectionService {\n    private static final String TAG = \"CollectionService\";\n\n    private final CollectionRepository repository;\n    private final String deviceUuid, csrfToken;\n    private final long userId;\n\n    private static CollectionService instance;\n\n    private CollectionService(final String deviceUuid,\n                              final String csrfToken,\n                              final long userId) {\n        this.deviceUuid = deviceUuid;\n        this.csrfToken = csrfToken;\n        this.userId = userId;\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(CollectionRepository.class);\n    }\n\n    public String getCsrfToken() {\n        return csrfToken;\n    }\n\n    public String getDeviceUuid() {\n        return deviceUuid;\n    }\n\n    public long getUserId() {\n        return userId;\n    }\n\n    public static CollectionService getInstance(final String deviceUuid, final String csrfToken, final long userId) {\n        if (instance == null\n                || !Objects.equals(instance.getCsrfToken(), csrfToken)\n                || !Objects.equals(instance.getDeviceUuid(), deviceUuid)\n                || !Objects.equals(instance.getUserId(), userId)) {\n            instance = new CollectionService(deviceUuid, csrfToken, userId);\n        }\n        return instance;\n    }\n\n    public void addPostsToCollection(final String collectionId,\n                                     final List<Media> posts,\n                                     final ServiceCallback<String> callback) {\n        final Map<String, Object> form = new HashMap<>(2);\n        form.put(\"module_name\", \"feed_saved_add_to_collection\");\n        final List<String> ids;\n        ids = posts.stream()\n                   .map(Media::getPk)\n                   .filter(Objects::nonNull)\n                   .collect(Collectors.toList());\n        form.put(\"added_media_ids\", \"[\" + TextUtils.join(\",\", ids) + \"]\");\n        changeCollection(collectionId, \"edit\", form, callback);\n    }\n\n    public void editCollectionName(final String collectionId,\n                                   final String name,\n                                   final ServiceCallback<String> callback) {\n        final Map<String, Object> form = new HashMap<>(1);\n        form.put(\"name\", name);\n        changeCollection(collectionId, \"edit\", form, callback);\n    }\n\n    public void deleteCollection(final String collectionId,\n                                 final ServiceCallback<String> callback) {\n        changeCollection(collectionId, \"delete\", null, callback);\n    }\n\n    public void changeCollection(final String collectionId,\n                                 final String action,\n                                 final Map<String, Object> options,\n                                 final ServiceCallback<String> callback) {\n        final Map<String, Object> form = new HashMap<>();\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"_uuid\", deviceUuid);\n        form.put(\"_uid\", userId);\n        if (options != null) form.putAll(options);\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> request = repository.changeCollection(collectionId, action, signedForm);\n        request.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                if (callback == null) return;\n                final String collectionsListResponse = response.body();\n                if (collectionsListResponse == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                callback.onSuccess(collectionsListResponse);\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/CommentService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.gson.Gson;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\n\nimport awais.instagrabber.models.Comment;\nimport awais.instagrabber.repositories.CommentRepository;\nimport awais.instagrabber.repositories.responses.ChildCommentsFetchResponse;\nimport awais.instagrabber.repositories.responses.CommentsFetchResponse;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class CommentService {\n    private static final String TAG = \"CommentService\";\n\n    private final CommentRepository repository;\n    private final String deviceUuid, csrfToken;\n    private final long userId;\n\n    private static CommentService instance;\n\n    private CommentService(final String deviceUuid,\n                           final String csrfToken,\n                           final long userId) {\n        this.deviceUuid = deviceUuid;\n        this.csrfToken = csrfToken;\n        this.userId = userId;\n        repository = RetrofitFactory.INSTANCE\n                .getRetrofit()\n                .create(CommentRepository.class);\n    }\n\n    public String getCsrfToken() {\n        return csrfToken;\n    }\n\n    public String getDeviceUuid() {\n        return deviceUuid;\n    }\n\n    public long getUserId() {\n        return userId;\n    }\n\n    public static CommentService getInstance(final String deviceUuid, final String csrfToken, final long userId) {\n        if (instance == null\n                || !Objects.equals(instance.getCsrfToken(), csrfToken)\n                || !Objects.equals(instance.getDeviceUuid(), deviceUuid)\n                || !Objects.equals(instance.getUserId(), userId)) {\n            instance = new CommentService(deviceUuid, csrfToken, userId);\n        }\n        return instance;\n    }\n\n    public void fetchComments(@NonNull final String mediaId,\n                              final String minId,\n                              @NonNull final ServiceCallback<CommentsFetchResponse> callback) {\n        final Map<String, String> form = new HashMap<>();\n        form.put(\"can_support_threading\", \"true\");\n        if (minId != null) form.put(\"min_id\", minId);\n        final Call<CommentsFetchResponse> request = repository.fetchComments(mediaId, form);\n        request.enqueue(new Callback<CommentsFetchResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<CommentsFetchResponse> call, @NonNull final Response<CommentsFetchResponse> response) {\n                callback.onSuccess(response.body());\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<CommentsFetchResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void fetchChildComments(@NonNull final String mediaId,\n                                   @NonNull final String commentId,\n                                   final String maxId,\n                                   @NonNull final ServiceCallback<ChildCommentsFetchResponse> callback) {\n        final Map<String, String> form = new HashMap<>();\n        if (maxId != null) form.put(\"max_id\", maxId);\n        final Call<ChildCommentsFetchResponse> request = repository.fetchChildComments(mediaId, commentId, form);\n        request.enqueue(new Callback<ChildCommentsFetchResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<ChildCommentsFetchResponse> call, @NonNull final Response<ChildCommentsFetchResponse> response) {\n                final ChildCommentsFetchResponse cfr = response.body();\n                if (cfr == null) callback.onFailure(new Exception(\"response is empty\"));\n                callback.onSuccess(cfr);\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<ChildCommentsFetchResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void comment(@NonNull final String mediaId,\n                        @NonNull final String comment,\n                        final String replyToCommentId,\n                        @NonNull final ServiceCallback<Comment> callback) {\n        final String module = \"self_comments_v2\";\n        final Map<String, Object> form = new HashMap<>();\n        // form.put(\"user_breadcrumb\", userBreadcrumb(comment.length()));\n        form.put(\"idempotence_token\", UUID.randomUUID().toString());\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"_uid\", userId);\n        form.put(\"_uuid\", deviceUuid);\n        form.put(\"comment_text\", comment);\n        form.put(\"containermodule\", module);\n        if (!TextUtils.isEmpty(replyToCommentId)) {\n            form.put(\"replied_to_comment_id\", replyToCommentId);\n        }\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> commentRequest = repository.comment(mediaId, signedForm);\n        commentRequest.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    Log.e(TAG, \"Error occurred while creating comment\");\n                    callback.onSuccess(null);\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    // final String status = jsonObject.optString(\"status\");\n                    final JSONObject commentJsonObject = jsonObject.optJSONObject(\"comment\");\n                    Comment comment = null;\n                    if (commentJsonObject != null) {\n                        final JSONObject userJsonObject = commentJsonObject.optJSONObject(\"user\");\n                        if (userJsonObject != null) {\n                            final Gson gson = new Gson();\n                            final User user = gson.fromJson(userJsonObject.toString(), User.class);\n                            comment = new Comment(\n                                    commentJsonObject.optString(\"pk\"),\n                                    commentJsonObject.optString(\"text\"),\n                                    commentJsonObject.optLong(\"created_at\"),\n                                    0L,\n                                    false,\n                                    user,\n                                    0\n                            );\n                        }\n                    }\n                    callback.onSuccess(comment);\n                } catch (Exception e) {\n                    callback.onFailure(e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void deleteComment(final String mediaId,\n                              final String commentId,\n                              @NonNull final ServiceCallback<Boolean> callback) {\n        deleteComments(mediaId, Collections.singletonList(commentId), callback);\n    }\n\n    public void deleteComments(final String mediaId,\n                               final List<String> commentIds,\n                               @NonNull final ServiceCallback<Boolean> callback) {\n        final Map<String, Object> form = new HashMap<>();\n        form.put(\"comment_ids_to_delete\", android.text.TextUtils.join(\",\", commentIds));\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"_uid\", userId);\n        form.put(\"_uuid\", deviceUuid);\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm);\n        bulkDeleteRequest.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    Log.e(TAG, \"Error occurred while deleting comments\");\n                    callback.onSuccess(false);\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    final String status = jsonObject.optString(\"status\");\n                    callback.onSuccess(status.equals(\"ok\"));\n                } catch (JSONException e) {\n                    // Log.e(TAG, \"Error parsing body\", e);\n                    callback.onFailure(e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                // Log.e(TAG, \"Error deleting comments\", t);\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void commentLike(@NonNull final String commentId,\n                            @NonNull final ServiceCallback<Boolean> callback) {\n        final Map<String, Object> form = new HashMap<>();\n        form.put(\"_csrftoken\", csrfToken);\n        // form.put(\"_uid\", userId);\n        // form.put(\"_uuid\", deviceUuid);\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> commentLikeRequest = repository.commentLike(commentId, signedForm);\n        commentLikeRequest.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    Log.e(TAG, \"Error occurred while liking comment\");\n                    callback.onSuccess(false);\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    final String status = jsonObject.optString(\"status\");\n                    callback.onSuccess(status.equals(\"ok\"));\n                } catch (JSONException e) {\n                    // Log.e(TAG, \"Error parsing body\", e);\n                    callback.onFailure(e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                Log.e(TAG, \"Error liking comment\", t);\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void commentUnlike(final String commentId,\n                              @NonNull final ServiceCallback<Boolean> callback) {\n        final Map<String, Object> form = new HashMap<>();\n        form.put(\"_csrftoken\", csrfToken);\n        // form.put(\"_uid\", userId);\n        // form.put(\"_uuid\", deviceUuid);\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> commentUnlikeRequest = repository.commentUnlike(commentId, signedForm);\n        commentUnlikeRequest.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    Log.e(TAG, \"Error occurred while unliking comment\");\n                    callback.onSuccess(false);\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    final String status = jsonObject.optString(\"status\");\n                    callback.onSuccess(status.equals(\"ok\"));\n                } catch (JSONException e) {\n                    // Log.e(TAG, \"Error parsing body\", e);\n                    callback.onFailure(e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                Log.e(TAG, \"Error unliking comment\", t);\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void translate(final String id,\n                          @NonNull final ServiceCallback<String> callback) {\n        final Map<String, String> form = new HashMap<>();\n        form.put(\"id\", String.valueOf(id));\n        form.put(\"type\", \"2\");\n        final Call<String> request = repository.translate(form);\n        request.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    Log.e(TAG, \"Error occurred while translating\");\n                    callback.onSuccess(null);\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    final String translation = jsonObject.optString(\"translation\");\n                    callback.onSuccess(translation);\n                } catch (JSONException e) {\n                    // Log.e(TAG, \"Error parsing body\", e);\n                    callback.onFailure(e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                Log.e(TAG, \"Error translating\", t);\n                callback.onFailure(t);\n            }\n        });\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/DirectMessagesRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.repositories.DirectMessagesService\nimport awais.instagrabber.repositories.requests.directmessages.*\nimport awais.instagrabber.repositories.responses.directmessages.*\nimport awais.instagrabber.repositories.responses.giphy.GiphyGif\nimport awais.instagrabber.utils.TextUtils.extractUrls\nimport awais.instagrabber.utils.Utils\nimport org.json.JSONArray\nimport java.util.*\n\nopen class DirectMessagesRepository(private val service: DirectMessagesService) {\n\n    suspend fun fetchInbox(\n        cursor: String?,\n        seqId: Long,\n    ): DirectInboxResponse {\n        val queryMap = mutableMapOf(\n            \"visual_message_return_type\" to \"unseen\",\n            \"thread_message_limit\" to 10.toString(),\n            \"persistentBadging\" to true.toString(),\n            \"limit\" to 10.toString(),\n        )\n        if (!cursor.isNullOrBlank()) {\n            queryMap[\"cursor\"] = cursor\n            queryMap[\"direction\"] = \"older\"\n        }\n        if (seqId != 0L) {\n            queryMap[\"seq_id\"] = seqId.toString()\n        }\n        return service.fetchInbox(queryMap)\n    }\n\n    suspend fun fetchThread(\n        threadId: String,\n        cursor: String?,\n    ): DirectThreadFeedResponse {\n        val queryMap = mutableMapOf(\n            \"visual_message_return_type\" to \"unseen\",\n            \"limit\" to 20.toString(),\n            \"direction\" to \"older\",\n        )\n        if (!cursor.isNullOrBlank()) {\n            queryMap[\"cursor\"] = cursor\n        }\n        return service.fetchThread(threadId, queryMap)\n    }\n\n    suspend fun fetchUnseenCount(): DirectBadgeCount = service.fetchUnseenCount()\n\n    suspend fun broadcastText(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        text: String,\n        repliedToItemId: String?,\n        repliedToClientContext: String?,\n    ): DirectThreadBroadcastResponse {\n        val urls = extractUrls(text)\n        if (urls.isNotEmpty()) {\n            return broadcastLink(\n                csrfToken,\n                userId,\n                deviceUuid,\n                clientContext,\n                threadIdsOrUserIds,\n                text,\n                urls,\n                repliedToItemId,\n                repliedToClientContext\n            )\n        }\n        val broadcastOptions = TextBroadcastOptions(clientContext, threadIdsOrUserIds, text)\n        if (!repliedToItemId.isNullOrBlank() && !repliedToClientContext.isNullOrBlank()) {\n            broadcastOptions.repliedToItemId = repliedToItemId\n            broadcastOptions.repliedToClientContext = repliedToClientContext\n        }\n        return broadcast(csrfToken, userId, deviceUuid, broadcastOptions)\n    }\n\n    private suspend fun broadcastLink(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        linkText: String,\n        urls: List<String>,\n        repliedToItemId: String?,\n        repliedToClientContext: String?,\n    ): DirectThreadBroadcastResponse {\n        val broadcastOptions = LinkBroadcastOptions(clientContext, threadIdsOrUserIds, linkText, urls)\n        if (!repliedToItemId.isNullOrBlank() && !repliedToClientContext.isNullOrBlank()) {\n            broadcastOptions.repliedToItemId = repliedToItemId\n            broadcastOptions.repliedToClientContext = repliedToClientContext\n        }\n        return broadcast(csrfToken, userId, deviceUuid, broadcastOptions)\n    }\n\n    suspend fun broadcastPhoto(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        uploadId: String,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, PhotoBroadcastOptions(clientContext, threadIdsOrUserIds, true, uploadId))\n\n    suspend fun broadcastVideo(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        uploadId: String,\n        videoResult: String,\n        sampled: Boolean,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, VideoBroadcastOptions(clientContext, threadIdsOrUserIds, videoResult, uploadId, sampled))\n\n    suspend fun broadcastVoice(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        uploadId: String,\n        waveform: List<Float>,\n        samplingFreq: Int,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, VoiceBroadcastOptions(clientContext, threadIdsOrUserIds, uploadId, waveform, samplingFreq))\n\n    suspend fun broadcastStoryReply(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        text: String,\n        mediaId: String,\n        reelId: String,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, StoryReplyBroadcastOptions(UUID.randomUUID().toString(), threadIdsOrUserIds, text, mediaId, reelId))\n\n    suspend fun broadcastReaction(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        itemId: String,\n        emoji: String?,\n        delete: Boolean,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, ReactionBroadcastOptions(clientContext, threadIdsOrUserIds, itemId, emoji, delete))\n\n    suspend fun broadcastAnimatedMedia(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        giphyGif: GiphyGif,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, AnimatedMediaBroadcastOptions(clientContext, threadIdsOrUserIds, giphyGif))\n\n    suspend fun broadcastMediaShare(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        mediaId: String,\n        childId: String?,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, MediaShareBroadcastOptions(clientContext, threadIdsOrUserIds, mediaId, childId))\n\n    suspend fun broadcastProfile(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        profileId: String,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, ProfileBroadcastOptions(clientContext, threadIdsOrUserIds, profileId))\n\n    suspend fun broadcastStory(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        clientContext: String,\n        threadIdsOrUserIds: ThreadIdsOrUserIds,\n        mediaId: String,\n        reelId: String,\n    ): DirectThreadBroadcastResponse =\n        broadcast(csrfToken, userId, deviceUuid, StoryBroadcastOptions(clientContext, threadIdsOrUserIds, mediaId, reelId))\n\n    private suspend fun broadcast(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        broadcastOptions: BroadcastOptions,\n    ): DirectThreadBroadcastResponse {\n        require(broadcastOptions.clientContext.isNotBlank()) { \"Broadcast requires a valid client context value\" }\n        val form = mutableMapOf<String, String>(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId.toString(10),\n            \"__uuid\" to deviceUuid,\n            \"client_context\" to broadcastOptions.clientContext,\n            \"mutation_token\" to broadcastOptions.clientContext,\n        )\n        val threadIds = broadcastOptions.threadIds\n        val userIds = broadcastOptions.userIds\n        require(!userIds.isNullOrEmpty() || !threadIds.isNullOrEmpty()) {\n            \"Either pass a list of thread ids or a list of lists of user ids\"\n        }\n        if (!threadIds.isNullOrEmpty()) {\n            form[\"thread_ids\"] = JSONArray(threadIds).toString()\n        }\n        if (!userIds.isNullOrEmpty()) {\n            form[\"recipient_users\"] = JSONArray(userIds).toString()\n        }\n        val repliedToItemId = broadcastOptions.repliedToItemId\n        val repliedToClientContext = broadcastOptions.repliedToClientContext\n        if (!repliedToItemId.isNullOrBlank() && !repliedToClientContext.isNullOrBlank()) {\n            form[\"replied_to_item_id\"] = repliedToItemId\n            form[\"replied_to_client_context\"] = repliedToClientContext\n        }\n        form.putAll(broadcastOptions.formMap)\n        form[\"action\"] = \"send_item\"\n//        val signedForm = Utils.sign(form)\n        return service.broadcast(broadcastOptions.itemType.value, form)\n    }\n\n    suspend fun addUsers(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: Collection<Long>,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n        )\n        return service.addUsers(threadId, form)\n    }\n\n    suspend fun removeUsers(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: Collection<Long>,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n        )\n        return service.removeUsers(threadId, form)\n    }\n\n    suspend fun updateTitle(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        title: String,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"title\" to title,\n        )\n        return service.updateTitle(threadId, form)\n    }\n\n    suspend fun addAdmins(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: Collection<Long>,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n        )\n        return service.addAdmins(threadId, form)\n    }\n\n    suspend fun removeAdmins(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: Collection<Long>,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n        )\n        return service.removeAdmins(threadId, form)\n    }\n\n    suspend fun deleteItem(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        itemId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.deleteItem(threadId, itemId, form)\n    }\n\n    suspend fun rankedRecipients(\n        mode: String?,\n        showThreads: Boolean?,\n        query: String?,\n    ): RankedRecipientsResponse {\n        // String correctedMode = mode;\n        // if (TextUtils.isEmpty(mode) || (!mode.equals(\"raven\") && !mode.equals(\"reshare\"))) {\n        //     correctedMode = \"raven\";\n        // }\n        val queryMap = mutableMapOf<String, String>()\n        if (!mode.isNullOrBlank()) {\n            queryMap[\"mode\"] = mode\n        }\n        if (!query.isNullOrBlank()) {\n            queryMap[\"query\"] = query\n        }\n        if (showThreads != null) {\n            queryMap[\"showThreads\"] = showThreads.toString()\n        }\n        return service.rankedRecipients(queryMap)\n    }\n\n    suspend fun forward(\n        toThreadId: String,\n        itemType: String,\n        fromThreadId: String,\n        itemId: String,\n    ): DirectThreadBroadcastResponse {\n        val form = mapOf(\n            \"action\" to \"forward_item\",\n            \"thread_id\" to toThreadId,\n            \"item_type\" to itemType,\n            \"forwarded_from_thread_id\" to fromThreadId,\n            \"forwarded_from_thread_item_id\" to itemId,\n        )\n        return service.forward(form)\n    }\n\n    suspend fun createThread(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        userIds: List<Long>,\n        threadTitle: String?,\n    ): DirectThread {\n        val userIdStringList = userIds.map { it.toString() }\n        val form = mutableMapOf<String, Any>(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"_uid\" to userId,\n            \"recipient_users\" to JSONArray(userIdStringList).toString(),\n        )\n        if (!threadTitle.isNullOrBlank()) {\n            form[\"thread_title\"] = threadTitle\n        }\n        val signedForm = Utils.sign(form)\n        return service.createThread(signedForm)\n    }\n\n    suspend fun mute(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid\n        )\n        return service.mute(threadId, form)\n    }\n\n    suspend fun unmute(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.unmute(threadId, form)\n    }\n\n    suspend fun muteMentions(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.muteMentions(threadId, form)\n    }\n\n    suspend fun unmuteMentions(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.unmuteMentions(threadId, form)\n    }\n\n    suspend fun participantRequests(\n        threadId: String,\n        pageSize: Int,\n        cursor: String? = null,\n    ): DirectThreadParticipantRequestsResponse {\n        return service.participantRequests(threadId, pageSize, cursor)\n    }\n\n    suspend fun approveParticipantRequests(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: List<Long>,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n            // \"share_join_chat_story\" to String.valueOf(true)\n        )\n        return service.approveParticipantRequests(threadId, form)\n    }\n\n    suspend fun declineParticipantRequests(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        userIds: List<Long>,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"user_ids\" to JSONArray(userIds).toString(),\n        )\n        return service.declineParticipantRequests(threadId, form)\n    }\n\n    suspend fun approvalRequired(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.approvalRequired(threadId, form)\n    }\n\n    suspend fun approvalNotRequired(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.approvalNotRequired(threadId, form)\n    }\n\n    suspend fun leave(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.leave(threadId, form)\n    }\n\n    suspend fun end(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): DirectThreadDetailsChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.end(threadId, form)\n    }\n\n    suspend fun fetchPendingInbox(cursor: String?, seqId: Long): DirectInboxResponse {\n        val queryMap = mutableMapOf(\n            \"visual_message_return_type\" to \"unseen\",\n            \"thread_message_limit\" to 20.toString(),\n            \"persistentBadging\" to true.toString(),\n            \"limit\" to 10.toString(),\n        )\n        if (!cursor.isNullOrBlank()) {\n            queryMap[\"cursor\"] = cursor\n            queryMap[\"direction\"] = \"older\"\n        }\n        if (seqId != 0L) {\n            queryMap[\"seq_id\"] = seqId.toString()\n        }\n        return service.fetchPendingInbox(queryMap)\n    }\n\n    suspend fun approveRequest(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.approveRequest(threadId, form)\n    }\n\n    suspend fun declineRequest(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n    ): String {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n        )\n        return service.declineRequest(threadId, form)\n    }\n\n    suspend fun markAsSeen(\n        csrfToken: String,\n        deviceUuid: String,\n        threadId: String,\n        directItem: DirectItem,\n    ): DirectItemSeenResponse? {\n        val itemId = directItem.itemId ?: return null\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"use_unified_inbox\" to \"true\",\n            \"action\" to \"mark_seen\",\n            \"thread_id\" to threadId,\n            \"item_id\" to itemId,\n        )\n        return service.markItemSeen(threadId, itemId, form)\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: DirectMessagesRepository? = null\n\n        fun getInstance(): DirectMessagesRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: DirectMessagesService = RetrofitFactory.retrofit.create(DirectMessagesService::class.java)\n                DirectMessagesRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/DiscoverService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableMap;\n\nimport java.util.Objects;\n\nimport awais.instagrabber.repositories.DiscoverRepository;\nimport awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;\nimport awais.instagrabber.utils.TextUtils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class DiscoverService {\n\n    private static final String TAG = \"DiscoverService\";\n\n    private final DiscoverRepository repository;\n\n    private static DiscoverService instance;\n\n    private DiscoverService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(DiscoverRepository.class);\n    }\n\n    public static DiscoverService getInstance() {\n        if (instance == null) {\n            instance = new DiscoverService();\n        }\n        return instance;\n    }\n\n    public void topicalExplore(@NonNull final TopicalExploreRequest request,\n                               final ServiceCallback<TopicalExploreFeedResponse> callback) {\n        final ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder()\n                .put(\"module\", \"explore_popular\");\n        if (!TextUtils.isEmpty(request.getModule())) {\n            builder.put(\"module\", request.getModule());\n        }\n        if (!TextUtils.isEmpty(request.getClusterId())) {\n            builder.put(\"cluster_id\", request.getClusterId());\n        }\n        if (!TextUtils.isEmpty(request.getMaxId())) {\n            builder.put(\"max_id\", request.getMaxId());\n        }\n        final Call<TopicalExploreFeedResponse> req = repository.topicalExplore(builder.build());\n        req.enqueue(new Callback<TopicalExploreFeedResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<TopicalExploreFeedResponse> call,\n                                   @NonNull final Response<TopicalExploreFeedResponse> response) {\n                if (callback == null) return;\n                final TopicalExploreFeedResponse feedResponse = response.body();\n                if (feedResponse == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                callback.onSuccess(feedResponse);\n                // try {\n                // } catch (JSONException e) {\n                //     callback.onFailure(e);\n                //     // Log.e(TAG, \"Error parsing topicalExplore response\", e);\n                // }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<TopicalExploreFeedResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    // private TopicalExploreResponse parseTopicalExploreResponse(@NonNull final String body) throws JSONException {\n    //     final JSONObject root = new JSONObject(body);\n    //     final boolean moreAvailable = root.optBoolean(\"more_available\");\n    //     final int nextMaxId = root.optInt(\"next_max_id\", -1);\n    //     final int numResults = root.optInt(\"num_results\");\n    //     final String status = root.optString(\"status\");\n    //     final JSONArray clustersJson = root.optJSONArray(\"clusters\");\n    //     final List<TopicCluster> clusters = parseClusters(clustersJson);\n    //     final JSONArray itemsJson = root.optJSONArray(\"items\");\n    //     final List<FeedModel> items = parseItems(itemsJson);\n    //     return new TopicalExploreResponse(\n    //             moreAvailable,\n    //             nextMaxId,\n    //             numResults,\n    //             status,\n    //             clusters,\n    //             items\n    //     );\n    // }\n\n    // private List<TopicCluster> parseClusters(final JSONArray clustersJson) throws JSONException {\n    //     if (clustersJson == null) {\n    //         return Collections.emptyList();\n    //     }\n    //     final List<TopicCluster> clusters = new ArrayList<>();\n    //     for (int i = 0; i < clustersJson.length(); i++) {\n    //         final JSONObject clusterJson = clustersJson.getJSONObject(i);\n    //         final String id = clusterJson.optString(\"id\");\n    //         final String title = clusterJson.optString(\"title\");\n    //         if (TextUtils.isEmpty(id) || TextUtils.isEmpty(title)) {\n    //             continue;\n    //         }\n    //         final String type = clusterJson.optString(\"type\");\n    //         final boolean canMute = clusterJson.optBoolean(\"can_mute\");\n    //         final boolean getMuted = clusterJson.optBoolean(\"is_muted\");\n    //         final JSONObject coverMediaJson = clusterJson.optJSONObject(\"cover_media\");\n    //         final int rankedPosition = clusterJson.optInt(\"ranked_position\");\n    //         final FeedModel feedModel = parseClusterCover(coverMediaJson);\n    //         final TopicCluster topicCluster = new TopicCluster(\n    //                 id,\n    //                 title,\n    //                 type,\n    //                 canMute,\n    //                 getMuted,\n    //                 rankedPosition,\n    //                 feedModel\n    //         );\n    //         clusters.add(topicCluster);\n    //     }\n    //     return clusters;\n    // }\n\n    // private FeedModel parseClusterCover(final JSONObject coverMediaJson) throws JSONException {\n    //     if (coverMediaJson == null) {\n    //         return null;\n    //     }\n    //     ProfileModel profileModel = null;\n    //     if (coverMediaJson.has(\"user\")) {\n    //         final JSONObject user = coverMediaJson.getJSONObject(\"user\");\n    //         profileModel = new ProfileModel(\n    //                 user.optBoolean(\"is_private\"),\n    //                 false,\n    //                 user.optBoolean(\"is_verified\"),\n    //                 user.getString(\"pk\"),\n    //                 user.getString(Constants.EXTRAS_USERNAME),\n    //                 user.optString(\"full_name\"),\n    //                 null,\n    //                 null,\n    //                 user.getString(\"profile_pic_url\"),\n    //                 null,\n    //                 0,\n    //                 0,\n    //                 0,\n    //                 false,\n    //                 false,\n    //                 false,\n    //                 false,\n    //                 false);\n    //     }\n    //     final String resourceUrl = ResponseBodyUtils.getHighQualityImage(coverMediaJson);\n    //     final String thumbnailUrl = ResponseBodyUtils.getLowQualityImage(coverMediaJson);\n    //     final int width = coverMediaJson.optInt(\"original_width\");\n    //     final int height = coverMediaJson.optInt(\"original_height\");\n    //     return new FeedModel.Builder()\n    //             .setProfileModel(profileModel)\n    //             .setItemType(MediaItemType.MEDIA_TYPE_IMAGE)\n    //             .setViewCount(0)\n    //             .setPostId(coverMediaJson.getString(Constants.EXTRAS_ID))\n    //             .setDisplayUrl(resourceUrl)\n    //             .setThumbnailUrl(thumbnailUrl)\n    //             .setShortCode(coverMediaJson.getString(\"code\"))\n    //             .setPostCaption(null)\n    //             .setCommentsCount(0)\n    //             .setTimestamp(coverMediaJson.optLong(\"taken_at\", -1))\n    //             .setLiked(false)\n    //             .setBookmarked(false)\n    //             .setLikesCount(0)\n    //             .setLocationName(null)\n    //             .setLocationId(null)\n    //             .setImageHeight(height)\n    //             .setImageWidth(width)\n    //             .build();\n    // }\n\n    // private List<FeedModel> parseItems(final JSONArray items) throws JSONException {\n    //     if (items == null) {\n    //         return Collections.emptyList();\n    //     }\n    //     final List<FeedModel> feedModels = new ArrayList<>();\n    //     for (int i = 0; i < items.length(); i++) {\n    //         final JSONObject itemJson = items.optJSONObject(i);\n    //         if (itemJson == null) {\n    //             continue;\n    //         }\n    //         final JSONObject mediaJson = itemJson.optJSONObject(\"media\");\n    //         final FeedModel feedModel = ResponseBodyUtils.parseItem(mediaJson);\n    //         if (feedModel != null) {\n    //             feedModels.add(feedModel);\n    //         }\n    //     }\n    //     return feedModels;\n    // }\n\n    public static class TopicalExploreRequest {\n\n        private String module;\n        private String clusterId;\n        private String maxId;\n\n        public TopicalExploreRequest() {}\n\n        public TopicalExploreRequest(final String module, final String clusterId, final String maxId) {\n            this.module = module;\n            this.clusterId = clusterId;\n            this.maxId = maxId;\n        }\n\n        public String getModule() {\n            return module;\n        }\n\n        public TopicalExploreRequest setModule(final String module) {\n            this.module = module;\n            return this;\n        }\n\n        public String getClusterId() {\n            return clusterId;\n        }\n\n        public void setClusterId(final String clusterId) {\n            this.clusterId = clusterId;\n        }\n\n        public String getMaxId() {\n            return maxId;\n        }\n\n        public void setMaxId(final String maxId) {\n            this.maxId = maxId;\n        }\n\n        @Override\n        public boolean equals(final Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            final TopicalExploreRequest that = (TopicalExploreRequest) o;\n            return maxId.equals(that.maxId) &&\n                    Objects.equals(module, that.module) &&\n                    Objects.equals(clusterId, that.clusterId);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(module, clusterId, maxId);\n        }\n\n        @NonNull\n        @Override\n        public String toString() {\n            return \"TopicalExploreRequest{\" +\n                    \"module='\" + module + '\\'' +\n                    \", clusterId='\" + clusterId + '\\'' +\n                    \", maxId=\" + maxId +\n                    '}';\n        }\n    }\n\n    // public static class TopicalExploreResponse {\n    //\n    //     private boolean moreAvailable;\n    //     private int nextMaxId;\n    //     private int numResults;\n    //     private String status;\n    //     private List<TopicCluster> clusters;\n    //     private List<FeedModel> items;\n    //\n    //     public TopicalExploreResponse() {}\n    //\n    //     public TopicalExploreResponse(final boolean moreAvailable,\n    //                                   final int nextMaxId,\n    //                                   final int numResults,\n    //                                   final String status,\n    //                                   final List<TopicCluster> clusters, final List<FeedModel> items) {\n    //         this.moreAvailable = moreAvailable;\n    //         this.nextMaxId = nextMaxId;\n    //         this.numResults = numResults;\n    //         this.status = status;\n    //         this.clusters = clusters;\n    //         this.items = items;\n    //     }\n    //\n    //     public boolean isMoreAvailable() {\n    //         return moreAvailable;\n    //     }\n    //\n    //     public TopicalExploreResponse setMoreAvailable(final boolean moreAvailable) {\n    //         this.moreAvailable = moreAvailable;\n    //         return this;\n    //     }\n    //\n    //     public int getNextMaxId() {\n    //         return nextMaxId;\n    //     }\n    //\n    //     public TopicalExploreResponse setNextMaxId(final int nextMaxId) {\n    //         this.nextMaxId = nextMaxId;\n    //         return this;\n    //     }\n    //\n    //     public int getNumResults() {\n    //         return numResults;\n    //     }\n    //\n    //     public TopicalExploreResponse setNumResults(final int numResults) {\n    //         this.numResults = numResults;\n    //         return this;\n    //     }\n    //\n    //     public String getStatus() {\n    //         return status;\n    //     }\n    //\n    //     public TopicalExploreResponse setStatus(final String status) {\n    //         this.status = status;\n    //         return this;\n    //     }\n    //\n    //     public List<TopicCluster> getClusters() {\n    //         return clusters;\n    //     }\n    //\n    //     public TopicalExploreResponse setClusters(final List<TopicCluster> clusters) {\n    //         this.clusters = clusters;\n    //         return this;\n    //     }\n    //\n    //     public List<FeedModel> getItems() {\n    //         return items;\n    //     }\n    //\n    //     public TopicalExploreResponse setItems(final List<FeedModel> items) {\n    //         this.items = items;\n    //         return this;\n    //     }\n    //\n    //     @Override\n    //     public boolean equals(final Object o) {\n    //         if (this == o) return true;\n    //         if (o == null || getClass() != o.getClass()) return false;\n    //         final TopicalExploreResponse that = (TopicalExploreResponse) o;\n    //         return moreAvailable == that.moreAvailable &&\n    //                 nextMaxId == that.nextMaxId &&\n    //                 numResults == that.numResults &&\n    //                 Objects.equals(status, that.status) &&\n    //                 Objects.equals(clusters, that.clusters) &&\n    //                 Objects.equals(items, that.items);\n    //     }\n    //\n    //     @Override\n    //     public int hashCode() {\n    //         return Objects.hash(moreAvailable, nextMaxId, numResults, status, clusters, items);\n    //     }\n    //\n    //     @Override\n    //     public String toString() {\n    //         return \"TopicalExploreResponse{\" +\n    //                 \"moreAvailable=\" + moreAvailable +\n    //                 \", nextMaxId=\" + nextMaxId +\n    //                 \", numResults=\" + numResults +\n    //                 \", status='\" + status + '\\'' +\n    //                 \", clusters=\" + clusters +\n    //                 \", items=\" + items +\n    //                 '}';\n    //     }\n    // }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/FeedService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport awais.instagrabber.repositories.FeedRepository;\nimport awais.instagrabber.repositories.responses.Media;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;\nimport awais.instagrabber.repositories.responses.feed.EndOfFeedGroup;\nimport awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet;\nimport awais.instagrabber.repositories.responses.feed.FeedFetchResponse;\nimport awais.instagrabber.utils.TextUtils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class FeedService {\n    private static final String TAG = \"FeedService\";\n\n    private final FeedRepository repository;\n\n    private static FeedService instance;\n\n    private FeedService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(FeedRepository.class);\n    }\n\n    public static FeedService getInstance() {\n        if (instance == null) {\n            instance = new FeedService();\n        }\n        return instance;\n    }\n\n    public void fetch(final String csrfToken,\n                      final String deviceUuid,\n                      final String cursor,\n                      final ServiceCallback<PostsFetchResponse> callback) {\n        final Map<String, String> form = new HashMap<>();\n        form.put(\"_uuid\", deviceUuid);\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"phone_id\", UUID.randomUUID().toString());\n        form.put(\"device_id\", UUID.randomUUID().toString());\n        form.put(\"client_session_id\", UUID.randomUUID().toString());\n        form.put(\"is_prefetch\", \"0\");\n        if (!TextUtils.isEmpty(cursor)) {\n            form.put(\"max_id\", cursor);\n            form.put(\"reason\", \"pagination\");\n        } else {\n            form.put(\"is_pull_to_refresh\", \"1\");\n            form.put(\"reason\", \"pull_to_refresh\");\n        }\n        final Call<FeedFetchResponse> request = repository.fetch(form);\n        request.enqueue(new Callback<FeedFetchResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<FeedFetchResponse> call, @NonNull final Response<FeedFetchResponse> response) {\n                try {\n                    // Log.d(TAG, \"onResponse: body: \" + response.body());\n                    final PostsFetchResponse postsFetchResponse = parseResponse(response);\n                    if (callback != null) {\n                        callback.onSuccess(postsFetchResponse);\n                    }\n                } catch (Exception e) {\n                    Log.e(TAG, \"onResponse\", e);\n                    if (callback != null) {\n                        callback.onFailure(e);\n                    }\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<FeedFetchResponse> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n\n    }\n\n    @NonNull\n    private PostsFetchResponse parseResponse(@NonNull final Response<FeedFetchResponse> response) {\n        final FeedFetchResponse feedFetchResponse = response.body();\n        if (feedFetchResponse == null) {\n            Log.e(TAG, \"parseResponse: feed response body is empty with status code: \" + response.code());\n            return new PostsFetchResponse(Collections.emptyList(), false, null);\n        }\n        return parseResponseBody(feedFetchResponse);\n    }\n\n    @NonNull\n    private PostsFetchResponse parseResponseBody(@NonNull final FeedFetchResponse feedFetchResponse) {\n        final boolean moreAvailable = feedFetchResponse.isMoreAvailable();\n        String nextMaxId = feedFetchResponse.getNextMaxId();\n        final boolean needNewMaxId = nextMaxId.equals(\"feed_recs_head_load\");\n        final List<Media> allPosts = new ArrayList<>();\n        final List<Media> items = feedFetchResponse.getItems();\n        for (final Media media : items) {\n            if (needNewMaxId && media.getEndOfFeedDemarcator() != null) {\n                final EndOfFeedDemarcator endOfFeedDemarcator = media.getEndOfFeedDemarcator();\n                final EndOfFeedGroupSet groupSet = endOfFeedDemarcator.getGroupSet();\n                if (groupSet == null) continue;\n                final List<EndOfFeedGroup> groups = groupSet.getGroups();\n                if (groups == null) continue;\n                for (final EndOfFeedGroup group : groups) {\n                    final String id = group.getId();\n                    if (id == null || !id.equals(\"past_posts\")) continue;\n                    nextMaxId = group.getNextMaxId();\n                    final List<Media> feedItems = group.getFeedItems();\n                    for (final Media feedItem : feedItems) {\n                        if (feedItem == null || feedItem.isInjected() || feedItem.getType() == null) continue;\n                        allPosts.add(feedItem);\n                    }\n                }\n                continue;\n            }\n            if (media == null || media.isInjected() || media.getType() == null) continue;\n            allPosts.add(media);\n        }\n        return new PostsFetchResponse(allPosts, moreAvailable, nextMaxId);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/FriendshipRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.repositories.FriendshipService\nimport awais.instagrabber.repositories.responses.FriendshipChangeResponse\nimport awais.instagrabber.repositories.responses.FriendshipListFetchResponse\nimport awais.instagrabber.repositories.responses.FriendshipRestrictResponse\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.webservices.RetrofitFactory.retrofit\n\nclass FriendshipRepository(private val service: FriendshipService) {\n\n    suspend fun follow(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, \"create\", targetUserId)\n\n    suspend fun unfollow(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, \"destroy\", targetUserId)\n\n    suspend fun changeBlock(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        unblock: Boolean,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, if (unblock) \"unblock\" else \"block\", targetUserId)\n\n    suspend fun toggleRestrict(\n        csrfToken: String,\n        deviceUuid: String,\n        targetUserId: Long,\n        restrict: Boolean,\n    ): FriendshipRestrictResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uuid\" to deviceUuid,\n            \"target_user_id\" to targetUserId.toString(),\n        )\n        val action = if (restrict) \"restrict\" else \"unrestrict\"\n        return service.toggleRestrict(action, form)\n    }\n\n    suspend fun approve(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, \"approve\", targetUserId)\n\n    suspend fun ignore(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, \"ignore\", targetUserId)\n\n    suspend fun removeFollower(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse = change(csrfToken, userId, deviceUuid, \"remove_follower\", targetUserId)\n\n    private suspend fun change(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        action: String,\n        targetUserId: Long,\n    ): FriendshipChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n            \"radio_type\" to \"wifi-none\",\n            \"user_id\" to targetUserId,\n        )\n        val signedForm = Utils.sign(form)\n        return service.change(action, targetUserId, signedForm)\n    }\n\n    suspend fun changeMute(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        unmute: Boolean,\n        targetUserId: Long,\n        story: Boolean,  // true for story, false for posts\n    ): FriendshipChangeResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId.toString(),\n            \"_uuid\" to deviceUuid,\n            (if (story) \"target_reel_author_id\" else \"target_posts_author_id\") to targetUserId.toString(),\n        )\n        return service.changeMute(\n            if (unmute) \"unmute_posts_or_story_from_follow\" else \"mute_posts_or_story_from_follow\",\n            form\n        )\n    }\n\n    suspend fun getList(\n        follower: Boolean,\n        targetUserId: Long,\n        maxId: String?,\n        query: String?\n    ): FriendshipListFetchResponse {\n        val queryMap: MutableMap<String, String> = mutableMapOf()\n        if (!maxId.isNullOrEmpty()) queryMap.set(\"max_id\", maxId)\n        if (!query.isNullOrEmpty()) queryMap.set(\"query\", query)\n        return service.getList(targetUserId, if (follower) \"followers\" else \"following\", queryMap.toMap())\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: FriendshipRepository? = null\n\n        fun getInstance(): FriendshipRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: FriendshipService = retrofit.create(FriendshipService::class.java)\n                FriendshipRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/GifService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport awais.instagrabber.repositories.GifRepository;\nimport awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;\nimport retrofit2.Call;\n\npublic class GifService {\n\n    private final GifRepository repository;\n\n    private static GifService instance;\n\n    private GifService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(GifRepository.class);\n    }\n\n    public static GifService getInstance() {\n        if (instance == null) {\n            instance = new GifService();\n        }\n        return instance;\n    }\n\n    public Call<GiphyGifResponse> searchGiphyGifs(final String query,\n                                                  final boolean includeGifs) {\n        final String mediaTypes = includeGifs ? \"[\\\"giphy_gifs\\\",\\\"giphy\\\"]\" : \"[\\\"giphy\\\"]\";\n        return repository.searchGiphyGifs(\"direct\", query, mediaTypes);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/GraphQLRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport android.util.Log\nimport awais.instagrabber.models.enums.FollowingType\nimport awais.instagrabber.repositories.GraphQLService\nimport awais.instagrabber.repositories.responses.*\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.ResponseBodyUtils\nimport awais.instagrabber.utils.extensions.TAG\nimport org.json.JSONException\nimport org.json.JSONObject\nimport java.util.*\n\n\nopen class GraphQLRepository(private val service: GraphQLService) {\n\n    // TODO convert string response to a response class\n    private suspend fun fetch(\n        queryHash: String,\n        variables: String,\n        arg1: String,\n        arg2: String,\n        backup: User?,\n    ): PostsFetchResponse {\n        val queryMap = mapOf(\n            \"query_hash\" to queryHash,\n            \"variables\" to variables,\n        )\n        val response = service.fetch(queryMap)\n        return parsePostResponse(response, arg1, arg2, backup)\n    }\n\n    suspend fun fetchLocationPosts(\n        locationId: Long,\n        maxId: String?,\n    ): PostsFetchResponse = fetch(\n        \"36bd0f2bf5911908de389b8ceaa3be6d\",\n        \"{\\\"id\\\":\\\"\" + locationId + \"\\\",\" + \"\\\"first\\\":25,\" + \"\\\"after\\\":\\\"\" + (maxId ?: \"\") + \"\\\"}\",\n        Constants.EXTRAS_LOCATION,\n        \"edge_location_to_media\",\n        null\n    )\n\n    suspend fun fetchHashtagPosts(\n        tag: String,\n        maxId: String?,\n    ): PostsFetchResponse = fetch(\n        \"9b498c08113f1e09617a1703c22b2f32\",\n        \"{\\\"tag_name\\\":\\\"\" + tag + \"\\\",\" + \"\\\"first\\\":25,\" + \"\\\"after\\\":\\\"\" + (maxId ?: \"\") + \"\\\"}\",\n        Constants.EXTRAS_HASHTAG,\n        \"edge_hashtag_to_media\",\n        null,\n    )\n\n    suspend fun fetchProfilePosts(\n        profileId: Long,\n        postsPerPage: Int,\n        maxId: String?,\n        backup: User?,\n    ): PostsFetchResponse = fetch(\n        \"02e14f6a7812a876f7d133c9555b1151\",\n        \"{\\\"id\\\":\\\"\" + profileId + \"\\\",\" + \"\\\"first\\\":\" + postsPerPage + \",\" + \"\\\"after\\\":\\\"\" + (maxId ?: \"\") + \"\\\"}\",\n        Constants.EXTRAS_USER,\n        \"edge_owner_to_timeline_media\",\n        backup,\n    )\n\n    suspend fun fetchTaggedPosts(\n        profileId: Long,\n        postsPerPage: Int,\n        maxId: String?,\n    ): PostsFetchResponse = fetch(\n        \"31fe64d9463cbbe58319dced405c6206\",\n        \"{\\\"id\\\":\\\"\" + profileId + \"\\\",\" + \"\\\"first\\\":\" + postsPerPage + \",\" + \"\\\"after\\\":\\\"\" + (maxId ?: \"\") + \"\\\"}\",\n        Constants.EXTRAS_USER,\n        \"edge_user_to_photos_of_you\",\n        null,\n    )\n\n    @Throws(JSONException::class)\n    private fun parsePostResponse(\n        response: String,\n        arg1: String,\n        arg2: String,\n        backup: User?,\n    ): PostsFetchResponse {\n        if (response.isBlank()) {\n            Log.e(TAG, \"parseResponse: feed response body is empty\")\n            return PostsFetchResponse(emptyList(), false, null)\n        }\n        return parseResponseBody(response, arg1, arg2, backup)\n    }\n\n    @Throws(JSONException::class)\n    private fun parseResponseBody(\n        body: String,\n        arg1: String,\n        arg2: String,\n        backup: User?,\n    ): PostsFetchResponse {\n        val items: MutableList<Media> = ArrayList()\n        val timelineFeed = JSONObject(body)\n            .getJSONObject(\"data\")\n            .getJSONObject(arg1)\n            .getJSONObject(arg2)\n        val endCursor: String?\n        val hasNextPage: Boolean\n        val pageInfo = timelineFeed.getJSONObject(\"page_info\")\n        if (pageInfo.has(\"has_next_page\")) {\n            hasNextPage = pageInfo.getBoolean(\"has_next_page\")\n            endCursor = if (hasNextPage) pageInfo.getString(\"end_cursor\") else null\n        } else {\n            hasNextPage = false\n            endCursor = null\n        }\n        val feedItems = timelineFeed.getJSONArray(\"edges\")\n        for (i in 0 until feedItems.length()) {\n            val itemJson = feedItems.optJSONObject(i) ?: continue\n            val media = ResponseBodyUtils.parseGraphQLItem(itemJson, backup)\n            if (media != null) {\n                items.add(media)\n            }\n        }\n        return PostsFetchResponse(items, hasNextPage, endCursor)\n    }\n\n    // TODO convert string response to a response class\n    suspend fun fetchCommentLikers(\n        commentId: String,\n        endCursor: String?,\n    ): GraphQLUserListFetchResponse {\n        val queryMap = mapOf(\n            \"query_hash\" to \"5f0b1f6281e72053cbc07909c8d154ae\",\n            \"variables\" to \"{\\\"comment_id\\\":\\\"\" + commentId + \"\\\",\" + \"\\\"first\\\":30,\" + \"\\\"after\\\":\\\"\" + (endCursor ?: \"\") + \"\\\"}\"\n        )\n        val response = service.fetch(queryMap)\n        val body = JSONObject(response)\n        val status = body.getString(\"status\")\n        val data = body.getJSONObject(\"data\").getJSONObject(\"comment\").getJSONObject(\"edge_liked_by\")\n        val pageInfo = data.getJSONObject(\"page_info\")\n        val newEndCursor = if (pageInfo.getBoolean(\"has_next_page\")) pageInfo.getString(\"end_cursor\") else null\n        val users = data.getJSONArray(\"edges\")\n        val usersLen = users.length()\n        val userModels: MutableList<User> = ArrayList()\n        for (j in 0 until usersLen) {\n            val userObject = users.getJSONObject(j).getJSONObject(\"node\")\n            userModels.add(\n                User(\n                    userObject.getLong(\"id\"),\n                    userObject.getString(\"username\"),\n                    userObject.optString(\"full_name\"),\n                    userObject.optBoolean(\"is_private\"),\n                    userObject.getString(\"profile_pic_url\"),\n                    userObject.optBoolean(\"is_verified\")\n                )\n            )\n        }\n        return GraphQLUserListFetchResponse(newEndCursor, status, userModels)\n    }\n\n    suspend fun fetchComments(\n        shortCodeOrCommentId: String?,\n        root: Boolean,\n        cursor: String?,\n    ): String {\n        val variables = mapOf(\n            (if (root) \"shortcode\" else \"comment_id\") to shortCodeOrCommentId,\n            \"first\" to 50,\n            \"after\" to (cursor ?: \"\")\n        )\n        val queryMap = mapOf(\n            \"query_hash\" to if (root) \"bc3296d1ce80a24b1b6e40b1e72903f5\" else \"51fdd02b67508306ad4484ff574a0b62\",\n            \"variables\" to JSONObject(variables).toString()\n        )\n        return service.fetch(queryMap)\n    }\n\n    // TODO convert string response to a response class\n    open suspend fun fetchUser(\n        username: String,\n    ): User? {\n        val response = service.getUser(username)\n        try {\n            val body = JSONObject(\n                response\n                    .split(\"<script type=\\\"text/javascript\\\">window._sharedData = \").get(1)\n                    .split(\"</script>\").get(0)\n                    .trim().replace(Regex(\"\\\\};$\"), \"}\")\n            )\n            val userJson = body\n                .getJSONObject(\"entry_data\")\n                .getJSONArray(\"ProfilePage\")\n                .getJSONObject(0)\n                .getJSONObject(\"graphql\")\n                .getJSONObject(Constants.EXTRAS_USER)\n            val isPrivate = userJson.getBoolean(\"is_private\")\n            val id = userJson.optLong(Constants.EXTRAS_ID, 0)\n            val timelineMedia = userJson.getJSONObject(\"edge_owner_to_timeline_media\")\n            // if (timelineMedia.has(\"edges\")) {\n            //     final JSONArray edges = timelineMedia.getJSONArray(\"edges\");\n            // }\n            var url: String? = userJson.optString(\"external_url\")\n            if (url.isNullOrBlank()) url = null\n            return User(\n                id,\n                username,\n                userJson.getString(\"full_name\"),\n                isPrivate,\n                userJson.getString(\"profile_pic_url_hd\"),\n                userJson.getBoolean(\"is_verified\"),\n                friendshipStatus = FriendshipStatus(\n                    userJson.optBoolean(\"followed_by_viewer\"),\n                    userJson.optBoolean(\"follows_viewer\"),\n                    userJson.optBoolean(\"blocked_by_viewer\"),\n                    false,\n                    isPrivate,\n                    userJson.optBoolean(\"has_requested_viewer\"),\n                    userJson.optBoolean(\"requested_by_viewer\"),\n                    false,\n                    userJson.optBoolean(\"restricted_by_viewer\"),\n                    false\n                ),\n                mediaCount = timelineMedia.getLong(\"count\"),\n                followerCount = userJson.getJSONObject(\"edge_followed_by\").getLong(\"count\"),\n                followingCount = userJson.getJSONObject(\"edge_follow\").getLong(\"count\"),\n                biography = userJson.getString(\"biography\"),\n                externalUrl = url,\n            )\n        }\n        catch (e: Exception) {\n            Log.e(TAG, \"fetchUser failed\", e)\n            return null\n        }\n    }\n\n    // TODO convert string response to a response class\n    suspend fun fetchPost(\n        shortcode: String,\n    ): Media {\n        val response = service.getPost(shortcode)\n        val body = JSONObject(response)\n        val media = body.getJSONObject(\"graphql\").getJSONObject(\"shortcode_media\")\n        return ResponseBodyUtils.parseGraphQLItem(media, null)\n    }\n\n    // TODO convert string response to a response class\n    suspend fun fetchTag(\n        tag: String,\n    ): Hashtag {\n        val response = service.getTag(tag)\n        val body = JSONObject(response\n            .split(\"<script type=\\\"text/javascript\\\">window._sharedData = \").get(1)\n            .split(\"</script>\").get(0)\n            .trim().replace(Regex(\"\\\\};$\"), \"}\"))\n            .getJSONObject(\"entry_data\")\n            .getJSONArray(\"TagPage\")\n            .getJSONObject(0)\n            .getJSONObject(\"graphql\")\n            .getJSONObject(Constants.EXTRAS_HASHTAG)\n        val timelineMedia = body.getJSONObject(\"edge_hashtag_to_media\")\n        return Hashtag(\n            body.getString(Constants.EXTRAS_ID),\n            body.getString(\"name\"),\n            timelineMedia.getLong(\"count\"),\n            if (body.optBoolean(\"is_following\")) FollowingType.FOLLOWING else FollowingType.NOT_FOLLOWING,\n            null\n        )\n    }\n\n    // TODO convert string response to a response class\n    suspend fun fetchLocation(\n        locationId: Long,\n    ): Location {\n        val response = service.getLocation(locationId)\n        val body = JSONObject(response\n            .split(\"<script type=\\\"text/javascript\\\">window._sharedData = \", \"</script>\").get(1)\n            .trim().replace(Regex(\"};$\"), \"}\"))\n            .getJSONObject(\"entry_data\")\n            .getJSONArray(\"LocationsPage\")\n            .getJSONObject(0)\n            .getJSONObject(\"graphql\")\n            .getJSONObject(Constants.EXTRAS_LOCATION)\n        // val timelineMedia = body.getJSONObject(\"edge_location_to_media\")\n        val address = JSONObject(body.getString(\"address_json\"))\n        return Location(\n            body.getLong(Constants.EXTRAS_ID),\n            body.getString(\"slug\"),\n            body.getString(\"name\"),\n            address.optString(\"street_address\"),\n            address.optString(\"city_name\"),\n            body.optDouble(\"lng\", 0.0),\n            body.optDouble(\"lat\", 0.0)\n        )\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: GraphQLRepository? = null\n\n        fun getInstance(): GraphQLRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: GraphQLService = RetrofitFactory.retrofitWeb.create(GraphQLService::class.java)\n                GraphQLRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/LocationService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableMap;\n\nimport awais.instagrabber.repositories.LocationRepository;\nimport awais.instagrabber.repositories.responses.Location;\nimport awais.instagrabber.repositories.responses.LocationFeedResponse;\nimport awais.instagrabber.repositories.responses.Place;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.utils.TextUtils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class LocationService {\n    private static final String TAG = \"LocationService\";\n\n    private final LocationRepository repository;\n\n    private static LocationService instance;\n\n    private LocationService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(LocationRepository.class);\n    }\n\n    public static LocationService getInstance() {\n        if (instance == null) {\n            instance = new LocationService();\n        }\n        return instance;\n    }\n\n    public void fetchPosts(final long locationId,\n                           final String maxId,\n                           final ServiceCallback<PostsFetchResponse> callback) {\n        final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();\n        if (!TextUtils.isEmpty(maxId)) {\n            builder.put(\"max_id\", maxId);\n        }\n        final Call<LocationFeedResponse> request = repository.fetchPosts(locationId, builder.build());\n        request.enqueue(new Callback<LocationFeedResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<LocationFeedResponse> call, @NonNull final Response<LocationFeedResponse> response) {\n                if (callback == null) return;\n                final LocationFeedResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                final PostsFetchResponse postsFetchResponse = new PostsFetchResponse(\n                        body.getItems(),\n                        body.getMoreAvailable(),\n                        body.getNextMaxId()\n                );\n                callback.onSuccess(postsFetchResponse);\n\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<LocationFeedResponse> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n    }\n\n    public void fetch(@NonNull final long locationId,\n                      final ServiceCallback<Location> callback) {\n        final Call<Place> request = repository.fetch(locationId);\n        request.enqueue(new Callback<Place>() {\n            @Override\n            public void onResponse(@NonNull final Call<Place> call, @NonNull final Response<Place> response) {\n                if (callback == null) {\n                    return;\n                }\n                callback.onSuccess(response.body() == null ? null : response.body().getLocation());\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<Place> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/MediaRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.models.enums.MediaItemType\nimport awais.instagrabber.repositories.MediaService\nimport awais.instagrabber.repositories.requests.Clip\nimport awais.instagrabber.repositories.requests.UploadFinishOptions\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.utils.DateUtils\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.retryContextString\nimport awais.instagrabber.webservices.RetrofitFactory.retrofit\nimport org.json.JSONObject\n\nclass MediaRepository(private val service: MediaService) {\n\n    suspend fun fetch(\n        mediaId: Long,\n    ): Media? {\n        val response = service.fetch(mediaId)\n        return if (response.items.isNullOrEmpty()) {\n            null\n        } else response.items[0]\n    }\n\n    suspend fun like(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        mediaId: String,\n    ): Boolean = action(csrfToken, userId, deviceUuid, mediaId, \"like\", null)\n\n    suspend fun unlike(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        mediaId: String,\n    ): Boolean = action(csrfToken, userId, deviceUuid, mediaId, \"unlike\", null)\n\n    suspend fun save(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        mediaId: String, collection: String?,\n    ): Boolean = action(csrfToken, userId, deviceUuid, mediaId, \"save\", collection)\n\n    suspend fun unsave(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        mediaId: String,\n    ): Boolean = action(csrfToken, userId, deviceUuid, mediaId, \"unsave\", null)\n\n    private suspend fun action(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        mediaId: String,\n        action: String,\n        collection: String?,\n    ): Boolean {\n        val form: MutableMap<String, Any> = mutableMapOf(\n            \"media_id\" to mediaId,\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n        )\n        // form.put(\"radio_type\", \"wifi-none\");\n        if (action == \"save\" && !collection.isNullOrBlank()) {\n            form[\"added_collection_ids\"] = \"[$collection]\"\n        }\n        // there also exists \"removed_collection_ids\" which can be used with \"save\" and \"unsave\"\n        val signedForm = Utils.sign(form)\n        val response = service.action(action, mediaId, signedForm)\n        val jsonObject = JSONObject(response)\n        val status = jsonObject.optString(\"status\")\n        return status == \"ok\"\n    }\n\n    suspend fun editCaption(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        postId: String,\n        newCaption: String,\n    ): Boolean {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n            \"igtv_feed_preview\" to \"false\",\n            \"media_id\" to postId,\n            \"caption_text\" to newCaption,\n        )\n        val signedForm = Utils.sign(form)\n        val response = service.editCaption(postId, signedForm)\n        val jsonObject = JSONObject(response)\n        val status = jsonObject.optString(\"status\")\n        return status == \"ok\"\n    }\n\n    suspend fun fetchLikes(\n        mediaId: String,\n        isComment: Boolean,\n    ): List<User> {\n        val response = service.fetchLikes(mediaId, if (isComment) \"comment_likers\" else \"likers\")\n        return response.users\n    }\n\n    suspend fun translate(\n        id: String,\n        type: String,  // 1 caption 2 comment 3 bio\n    ): String? {\n        val form = mapOf(\n            \"id\" to id,\n            \"type\" to type,\n        )\n        val response = service.translate(form)\n        val jsonObject = JSONObject(response)\n        if (!jsonObject.has(\"translation\") || jsonObject.isNull(\"translation\")) {\n            return null\n        }\n        return jsonObject.getString(\"translation\")\n    }\n\n    suspend fun uploadFinish(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        options: UploadFinishOptions,\n    ): String {\n        if (options.videoOptions != null) {\n            val videoOptions = options.videoOptions\n            if (videoOptions.clips.isEmpty()) {\n                videoOptions.clips = listOf(Clip(videoOptions.length, options.sourceType))\n            }\n        }\n        val timezoneOffset = DateUtils.timezoneOffset.toString()\n        val form = mutableMapOf<String, Any>(\n            \"timezone_offset\" to timezoneOffset,\n            \"_csrftoken\" to csrfToken,\n            \"source_type\" to options.sourceType,\n            \"_uid\" to userId.toString(),\n            \"_uuid\" to deviceUuid,\n            \"upload_id\" to options.uploadId,\n        )\n        if (options.videoOptions != null) {\n            form.putAll(options.videoOptions.map)\n        }\n        val queryMap = if (options.videoOptions != null) mapOf(\"video\" to \"1\") else emptyMap()\n        val signedForm = Utils.sign(form)\n        return service.uploadFinish(retryContextString, queryMap, signedForm)\n    }\n\n    suspend fun delete(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        postId: String,\n        type: MediaItemType,\n    ): String? {\n        if (!DELETABLE_ITEMS_TYPES.contains(type)) return null\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n            \"igtv_feed_preview\" to \"false\",\n            \"media_id\" to postId,\n        )\n        val signedForm = Utils.sign(form)\n        val mediaType: String = when (type) {\n            MediaItemType.MEDIA_TYPE_IMAGE -> \"PHOTO\"\n            MediaItemType.MEDIA_TYPE_VIDEO -> \"VIDEO\"\n            MediaItemType.MEDIA_TYPE_SLIDER -> \"CAROUSEL\"\n            else -> return null\n        }\n        return service.delete(postId, mediaType, signedForm)\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: MediaRepository? = null\n\n        private val DELETABLE_ITEMS_TYPES = listOf(\n            MediaItemType.MEDIA_TYPE_IMAGE,\n            MediaItemType.MEDIA_TYPE_VIDEO,\n            MediaItemType.MEDIA_TYPE_SLIDER\n        )\n\n        fun getInstance(): MediaRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: MediaService = retrofit.create(MediaService::class.java)\n                MediaRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/NewsService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport awais.instagrabber.repositories.NewsRepository;\nimport awais.instagrabber.repositories.responses.AymlResponse;\nimport awais.instagrabber.repositories.responses.AymlUser;\nimport awais.instagrabber.repositories.responses.NewsInboxResponse;\nimport awais.instagrabber.repositories.responses.User;\nimport awais.instagrabber.repositories.responses.UserSearchResponse;\nimport awais.instagrabber.repositories.responses.notification.Notification;\nimport awais.instagrabber.repositories.responses.notification.NotificationArgs;\nimport awais.instagrabber.repositories.responses.notification.NotificationCounts;\nimport awais.instagrabber.utils.Constants;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class NewsService {\n    private static final String TAG = \"NewsService\";\n\n    private final NewsRepository repository;\n\n    private static NewsService instance;\n\n    private NewsService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(NewsRepository.class);\n    }\n\n    public static NewsService getInstance() {\n        if (instance == null) {\n            instance = new NewsService();\n        }\n        return instance;\n    }\n\n    public void fetchAppInbox(final boolean markAsSeen,\n                              final ServiceCallback<List<Notification>> callback) {\n        final Call<NewsInboxResponse> request = repository.appInbox(markAsSeen, Constants.X_IG_APP_ID);\n        request.enqueue(new Callback<NewsInboxResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<NewsInboxResponse> call, @NonNull final Response<NewsInboxResponse> response) {\n                final NewsInboxResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                final List<Notification> result = new ArrayList<Notification>();\n                final List<Notification> newStories = body.getNewStories();\n                if (newStories != null) result.addAll(newStories);\n                final List<Notification> oldStories = body.getOldStories();\n                if (oldStories != null) result.addAll(oldStories);\n                callback.onSuccess(result);\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<NewsInboxResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n                // Log.e(TAG, \"onFailure: \", t);\n            }\n        });\n    }\n\n    public void fetchActivityCounts(final ServiceCallback<NotificationCounts> callback) {\n        final Call<NewsInboxResponse> request = repository.appInbox(false, null);\n        request.enqueue(new Callback<NewsInboxResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<NewsInboxResponse> call, @NonNull final Response<NewsInboxResponse> response) {\n                final NewsInboxResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                callback.onSuccess(body.getCounts());\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<NewsInboxResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n                // Log.e(TAG, \"onFailure: \", t);\n            }\n        });\n    }\n\n    public void fetchSuggestions(final String csrfToken,\n                                 final String deviceUuid,\n                                 final ServiceCallback<List<Notification>> callback) {\n        final Map<String, String> form = new HashMap<>();\n        form.put(\"_uuid\", UUID.randomUUID().toString());\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"phone_id\", UUID.randomUUID().toString());\n        form.put(\"device_id\", UUID.randomUUID().toString());\n        form.put(\"module\", \"discover_people\");\n        form.put(\"paginate\", \"false\");\n        final Call<AymlResponse> request = repository.getAyml(form);\n        request.enqueue(new Callback<AymlResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<AymlResponse> call, @NonNull final Response<AymlResponse> response) {\n                final AymlResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                final List<AymlUser> aymlUsers = new ArrayList<AymlUser>();\n                final List<AymlUser> newSuggestions = body.getNewSuggestedUsers().getSuggestions();\n                if (newSuggestions != null) {\n                    aymlUsers.addAll(newSuggestions);\n                }\n                final List<AymlUser> oldSuggestions = body.getSuggestedUsers().getSuggestions();\n                if (oldSuggestions != null) {\n                    aymlUsers.addAll(oldSuggestions);\n                }\n\n                final List<Notification> newsItems = aymlUsers\n                        .stream()\n                        .map(i -> {\n                            final User u = i.getUser();\n                            return new Notification(\n                                    new NotificationArgs(\n                                            i.getSocialContext(),\n                                            i.getAlgorithm(),\n                                            u.getPk(),\n                                            u.getProfilePicUrl(),\n                                            null,\n                                            0L,\n                                            u.getUsername(),\n                                            u.getFullName(),\n                                            u.isVerified()\n                                    ),\n                                    9999,\n                                    String.valueOf(u.getPk()) // placeholder\n                            );\n                        })\n                        .collect(Collectors.toList());\n                callback.onSuccess(newsItems);\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<AymlResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n                // Log.e(TAG, \"onFailure: \", t);\n            }\n        });\n    }\n\n    public void fetchChaining(final long targetId, final ServiceCallback<List<Notification>> callback) {\n        final Call<UserSearchResponse> request = repository.getChaining(targetId);\n        request.enqueue(new Callback<UserSearchResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<UserSearchResponse> call, @NonNull final Response<UserSearchResponse> response) {\n                final UserSearchResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n\n                final List<Notification> newsItems = body\n                        .getUsers()\n                        .stream()\n                        .map(u -> {\n                            return new Notification(\n                                    new NotificationArgs(\n                                            u.getSocialContext(),\n                                            null,\n                                            u.getPk(),\n                                            u.getProfilePicUrl(),\n                                            null,\n                                            0L,\n                                            u.getUsername(),\n                                            u.getFullName(),\n                                            u.isVerified()\n                                    ),\n                                    9999,\n                                    u.getProfilePicId() // placeholder\n                            );\n                        })\n                        .collect(Collectors.toList());\n                callback.onSuccess(newsItems);\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<UserSearchResponse> call, @NonNull final Throwable t) {\n                callback.onFailure(t);\n                // Log.e(TAG, \"onFailure: \", t);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/ProfileRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.repositories.ProfileService\nimport awais.instagrabber.repositories.responses.Media\nimport awais.instagrabber.repositories.responses.PostsFetchResponse\nimport awais.instagrabber.repositories.responses.WrappedMedia\nimport awais.instagrabber.repositories.responses.saved.CollectionsListResponse\nimport awais.instagrabber.utils.Utils\nimport com.google.common.collect.ImmutableMap\nimport java.util.*\nimport java.util.stream.Collectors\n\nclass ProfileRepository private constructor(private val repository: ProfileService) {\n    suspend fun fetchPosts(\n        userId: Long,\n        maxId: String?\n    ): PostsFetchResponse? {\n        val builder = ImmutableMap.builder<String?, String?>()\n        if (!maxId.isNullOrEmpty()) {\n            builder.put(\"max_id\", maxId)\n        }\n        val body = repository.fetch(userId, builder.build()) ?: return null\n        return PostsFetchResponse(\n            body.items,\n            body.moreAvailable,\n            body.nextMaxId\n        )\n    }\n\n    suspend fun fetchSaved(maxId: String?, collectionId: String): PostsFetchResponse? {\n        val builder = ImmutableMap.builder<String?, String?>()\n        if (!maxId.isNullOrEmpty()) {\n            builder.put(\"max_id\", maxId)\n        }\n        val userFeedResponse = if (collectionId.isNullOrEmpty() || collectionId == \"ALL_MEDIA_AUTO_COLLECTION\")\n                (repository.fetchSaved(builder.build()) ?: return null)\n            else repository.fetchSavedCollection(collectionId, builder.build()) ?: return null\n        val items = userFeedResponse.items\n        val posts: List<Media> = if (items == null) {\n            emptyList()\n        } else {\n            items.stream()\n                .map(WrappedMedia::media)\n                .filter { obj: Media? -> Objects.nonNull(obj) }\n                .collect(Collectors.toList())\n        }\n        return PostsFetchResponse(\n            posts,\n            userFeedResponse.isMoreAvailable,\n            userFeedResponse.nextMaxId\n        )\n    }\n\n    suspend fun fetchCollections(maxId: String?): CollectionsListResponse? {\n        val builder = ImmutableMap.builder<String?, String?>()\n        if (!maxId.isNullOrEmpty()) {\n            builder.put(\"max_id\", maxId)\n        }\n        builder.put(\n            \"collection_types\",\n            \"[\\\"ALL_MEDIA_AUTO_COLLECTION\\\",\\\"MEDIA\\\",\\\"PRODUCT_AUTO_COLLECTION\\\"]\"\n        )\n        return repository.fetchCollections(builder.build())\n    }\n\n    suspend fun createCollection(\n        name: String,\n        deviceUuid: String,\n        userId: Long,\n        csrfToken: String\n    ): String? {\n        val form: MutableMap<String, Any> = HashMap(6)\n        form[\"_csrftoken\"] = csrfToken\n        form[\"_uuid\"] = deviceUuid\n        form[\"_uid\"] = userId\n        form[\"collection_visibility\"] =\n            \"0\" // 1 for public, planned for future but currently inexistant\n        form[\"module_name\"] = \"collection_create\"\n        form[\"name\"] = name\n        val signedForm = Utils.sign(form)\n        return repository.createCollection(signedForm)\n    }\n\n    suspend fun fetchLiked(maxId: String?): PostsFetchResponse? {\n        val builder = ImmutableMap.builder<String?, String?>()\n        if (!maxId.isNullOrEmpty()) {\n            builder.put(\"max_id\", maxId)\n        }\n        val userFeedResponse = repository.fetchLiked(builder.build()) ?: return null\n        return PostsFetchResponse(\n            userFeedResponse.items,\n            userFeedResponse.moreAvailable,\n            userFeedResponse.nextMaxId\n        )\n    }\n\n    suspend fun fetchTagged(profileId: Long, maxId: String?): PostsFetchResponse? {\n        val builder = ImmutableMap.builder<String?, String?>()\n        if (!maxId.isNullOrEmpty()) {\n            builder.put(\"max_id\", maxId)\n        }\n        val userFeedResponse = repository.fetchTagged(profileId, builder.build()) ?: return null\n        return PostsFetchResponse(\n            userFeedResponse.items,\n            userFeedResponse.moreAvailable,\n            userFeedResponse.nextMaxId\n        )\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: ProfileRepository? = null\n\n        fun getInstance(): ProfileRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: ProfileService = RetrofitFactory.retrofit.create(ProfileService::class.java)\n                ProfileRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/RetrofitFactory.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.repositories.responses.Caption\nimport awais.instagrabber.repositories.serializers.CaptionDeserializer\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.webservices.interceptors.AddCookiesInterceptor\nimport awais.instagrabber.webservices.interceptors.IgErrorsInterceptor\n//import awais.instagrabber.webservices.interceptors.LoggingInterceptor\nimport com.google.gson.FieldNamingPolicy\nimport com.google.gson.GsonBuilder\nimport okhttp3.Cache\nimport okhttp3.OkHttpClient\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\nimport retrofit2.converter.scalars.ScalarsConverterFactory\nimport java.io.File\n\nobject RetrofitFactory {\n    private const val cacheSize: Long = 10 * 1024 * 1024 // 10 MB\n    private val cache = Cache(File(Utils.cacheDir), cacheSize)\n    private val igErrorsInterceptor: IgErrorsInterceptor by lazy { IgErrorsInterceptor() }\n\n    private val retrofitBuilder: Retrofit.Builder by lazy {\n        val clientBuilder = OkHttpClient.Builder().apply {\n            followRedirects(false)\n            followSslRedirects(false)\n            cache(cache)\n            addInterceptor(AddCookiesInterceptor())\n            addInterceptor(igErrorsInterceptor)\n            if (BuildConfig.DEBUG) {\n                // addInterceptor(LoggingInterceptor())\n            }\n        }\n        val gson = GsonBuilder().apply {\n            setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)\n            registerTypeAdapter(Caption::class.java, CaptionDeserializer())\n            setLenient()\n        }.create()\n        Retrofit.Builder().apply {\n            addConverterFactory(ScalarsConverterFactory.create())\n            addConverterFactory(GsonConverterFactory.create(gson))\n            client(clientBuilder.build())\n        }\n    }\n\n    val retrofit: Retrofit by lazy {\n        retrofitBuilder\n            .baseUrl(\"https://i.instagram.com\")\n            .build()\n    }\n\n    val retrofitWeb: Retrofit by lazy {\n        retrofitBuilder\n            .baseUrl(\"https://www.instagram.com\")\n            .build()\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/SearchRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.repositories.SearchService\nimport awais.instagrabber.repositories.responses.search.SearchResponse\nimport awais.instagrabber.webservices.RetrofitFactory.retrofitWeb\nimport com.google.common.collect.ImmutableMap\nimport retrofit2.Call\n\nclass SearchRepository(private val service: SearchService) {\n    suspend fun search(\n        isLoggedIn: Boolean,\n        query: String,\n        context: String\n    ): SearchResponse {\n        val builder = ImmutableMap.builder<String, String>()\n        builder.put(\"query\", query)\n        // context is one of: \"blended\", \"user\", \"place\", \"hashtag\"\n        // note that \"place\" and \"hashtag\" can contain ONE user result, who knows why\n        builder.put(\"context\", context)\n        builder.put(\"count\", \"50\")\n        return service.search(\n            if (isLoggedIn) \"https://i.instagram.com/api/v1/fbsearch/topsearch_flat/\" else \"https://www.instagram.com/web/search/topsearch/\",\n            builder.build()\n        )\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: SearchRepository? = null\n\n        fun getInstance(): SearchRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: SearchService = RetrofitFactory.retrofit.create(SearchService::class.java)\n                SearchRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/ServiceCallback.java",
    "content": "package awais.instagrabber.webservices;\n\npublic interface ServiceCallback<T> {\n    void onSuccess(T result);\n\n    void onFailure(Throwable t);\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/StoriesRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.fragments.settings.PreferenceKeys\nimport awais.instagrabber.repositories.StoriesService\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.stories.ArchiveResponse\nimport awais.instagrabber.repositories.responses.stories.Story\nimport awais.instagrabber.repositories.responses.stories.StoryMedia\nimport awais.instagrabber.repositories.responses.stories.StoryStickerResponse\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.webservices.RetrofitFactory.retrofit\nimport java.util.UUID\n\nopen class StoriesRepository(private val service: StoriesService) {\n\n    suspend fun fetch(mediaId: Long): StoryMedia? {\n        val response = service.fetch(mediaId)\n        return response.items?.get(0)\n    }\n\n    suspend fun getFeedStories(): List<Story> {\n        val response = service.getFeedStories()\n        val result: MutableList<Story> = mutableListOf()\n        if (response?.broadcasts != null) {\n            val length = response.broadcasts.size\n            for (i in 0 until length) {\n                val broadcast = response.broadcasts.get(i)\n                result.add(\n                    Story(\n                        broadcast.id,\n                        broadcast.publishedTime,\n                        1,\n                        0L,\n                        broadcast.broadcastOwner,\n                        broadcast.muted,\n                        false, // unclear\n                        null,\n                        null,\n                        null,\n                        null,\n                        broadcast\n                    )\n                )\n            }\n        }\n        if (response?.tray != null) result.addAll(response.tray)\n        return sort(result.toList())\n    }\n\n    open suspend fun fetchHighlights(profileId: Long): List<Story> {\n        val response = service.fetchHighlights(profileId)\n        val highlightModels = response?.tray ?: listOf()\n        return highlightModels\n    }\n\n    suspend fun fetchArchive(maxId: String): ArchiveResponse? {\n        val form = mutableMapOf(\n            \"include_suggested_highlights\" to \"false\",\n            \"is_in_archive_home\" to \"true\",\n            \"include_cover\" to \"1\",\n        )\n        if (!maxId.isNullOrEmpty()) {\n            form[\"max_id\"] = maxId // NOT TESTED\n        }\n        return service.fetchArchive(form)\n    }\n\n    open suspend fun getStories(options: StoryViewerOptions): Story? {\n        return when (options.type) {\n            StoryViewerOptions.Type.HIGHLIGHT,\n            StoryViewerOptions.Type.STORY_ARCHIVE\n            -> {\n                val response = service.getReelsMedia(options.name)\n                response.reels?.get(options.name)\n            }\n            StoryViewerOptions.Type.USER -> {\n                val response = service.getUserStories(options.id)\n                response.reel\n            }\n            // should not reach beyond this point\n            StoryViewerOptions.Type.LOCATION -> {\n                val response = service.getStories(\"locations\", options.id.toString())\n                response.story\n            }\n            StoryViewerOptions.Type.HASHTAG -> {\n                val response = service.getStories(\"tags\", options.name)\n                response.story\n            }\n            else -> null\n        }\n    }\n\n    private suspend fun respondToSticker(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyId: Long,\n        stickerId: Long,\n        action: String,\n        arg1: String,\n        arg2: String,\n    ): StoryStickerResponse {\n        val form = mapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n            \"mutation_token\" to UUID.randomUUID().toString(),\n            \"client_context\" to UUID.randomUUID().toString(),\n            \"radio_type\" to \"wifi-none\",\n            arg1 to arg2,\n        )\n        val signedForm = Utils.sign(form)\n        return service.respondToSticker(storyId, stickerId, action, signedForm)\n    }\n\n    suspend fun respondToQuestion(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyId: Long,\n        stickerId: Long,\n        answer: String,\n    ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, \"story_question_response\", \"response\", answer)\n\n    suspend fun respondToQuiz(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyId: Long,\n        stickerId: Long,\n        answer: Int,\n    ): StoryStickerResponse {\n        return respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, \"story_quiz_answer\", \"answer\", answer.toString())\n    }\n\n    suspend fun respondToPoll(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyId: Long,\n        stickerId: Long,\n        answer: Int,\n    ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, \"story_poll_vote\", \"vote\", answer.toString())\n\n    suspend fun respondToSlider(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyId: Long,\n        stickerId: Long,\n        answer: Double,\n    ): StoryStickerResponse = respondToSticker(csrfToken, userId, deviceUuid, storyId, stickerId, \"story_slider_vote\", \"vote\", answer.toString())\n\n    suspend fun seen(\n        csrfToken: String,\n        userId: Long,\n        deviceUuid: String,\n        storyMediaId: String,\n        takenAt: Long,\n        seenAt: Long,\n    ): String {\n        val reelsForm = mapOf(storyMediaId to listOf(takenAt.toString() + \"_\" + seenAt))\n        val form = mutableMapOf(\n            \"_csrftoken\" to csrfToken,\n            \"_uid\" to userId,\n            \"_uuid\" to deviceUuid,\n            \"container_module\" to \"feed_timeline\",\n            \"reels\" to reelsForm,\n        )\n        val signedForm = Utils.sign(form)\n        val queryMap = mapOf(\n            \"reel\" to \"1\",\n            \"live_vod\" to \"0\",\n        )\n        return service.seen(queryMap, signedForm)\n    }\n\n    private fun sort(list: List<Story>): List<Story> {\n        val listCopy = ArrayList(list)\n        listCopy.sortWith { o1, o2 ->\n            if (o1.latestReelMedia == null || o2.latestReelMedia == null) return@sortWith 0\n            else when (Utils.settingsHelper.getString(PreferenceKeys.STORY_SORT)) {\n                \"1\" -> return@sortWith o2.latestReelMedia.compareTo(o1.latestReelMedia)\n                \"2\" -> return@sortWith o1.latestReelMedia.compareTo(o2.latestReelMedia)\n                else -> return@sortWith 0\n            }\n        }\n        return listCopy\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: StoriesRepository? = null\n\n        fun getInstance(): StoriesRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: StoriesService = retrofit.create(StoriesService::class.java)\n                StoriesRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/TagsService.java",
    "content": "package awais.instagrabber.webservices;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport com.google.common.collect.ImmutableMap;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport awais.instagrabber.repositories.TagsRepository;\nimport awais.instagrabber.repositories.responses.Hashtag;\nimport awais.instagrabber.repositories.responses.PostsFetchResponse;\nimport awais.instagrabber.repositories.responses.TagFeedResponse;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n\npublic class TagsService {\n\n    private static final String TAG = \"TagsService\";\n\n    private static TagsService instance;\n\n    private final TagsRepository repository;\n\n    private TagsService() {\n        repository = RetrofitFactory.INSTANCE\n                                    .getRetrofit()\n                                    .create(TagsRepository.class);\n    }\n\n    public static TagsService getInstance() {\n        if (instance == null) {\n            instance = new TagsService();\n        }\n        return instance;\n    }\n\n    public void fetch(@NonNull final String tag,\n                      final ServiceCallback<Hashtag> callback) {\n        final Call<Hashtag> request = repository.fetch(tag);\n        request.enqueue(new Callback<Hashtag>() {\n            @Override\n            public void onResponse(@NonNull final Call<Hashtag> call, @NonNull final Response<Hashtag> response) {\n                if (callback == null) {\n                    return;\n                }\n                callback.onSuccess(response.body());\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<Hashtag> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n    }\n\n    public void changeFollow(@NonNull final String action,\n                             @NonNull final String tag,\n                             @NonNull final String csrfToken,\n                             @NonNull final long userId,\n                             @NonNull final String deviceUuid,\n                             final ServiceCallback<Boolean> callback) {\n        final Map<String, Object> form = new HashMap<>(3);\n        form.put(\"_csrftoken\", csrfToken);\n        form.put(\"_uid\", userId);\n        form.put(\"_uuid\", deviceUuid);\n        final Map<String, String> signedForm = Utils.sign(form);\n        final Call<String> request = repository.changeFollow(signedForm, action, tag);\n        request.enqueue(new Callback<String>() {\n            @Override\n            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {\n                final String body = response.body();\n                if (body == null) {\n                    callback.onFailure(new RuntimeException(\"body is null\"));\n                    return;\n                }\n                try {\n                    final JSONObject jsonObject = new JSONObject(body);\n                    final String status = jsonObject.optString(\"status\");\n                    callback.onSuccess(status.equals(\"ok\"));\n                } catch (JSONException e) {\n                    Log.e(TAG, \"onResponse: \", e);\n                }\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {\n                // Log.e(TAG, \"onFailure: \", t);\n                callback.onFailure(t);\n            }\n        });\n    }\n\n    public void fetchPosts(@NonNull final String tag,\n                           final String maxId,\n                           final ServiceCallback<PostsFetchResponse> callback) {\n        final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();\n        if (!TextUtils.isEmpty(maxId)) {\n            builder.put(\"max_id\", maxId);\n        }\n        final Call<TagFeedResponse> request = repository.fetchPosts(tag, builder.build());\n        request.enqueue(new Callback<TagFeedResponse>() {\n            @Override\n            public void onResponse(@NonNull final Call<TagFeedResponse> call, @NonNull final Response<TagFeedResponse> response) {\n                if (callback == null) {\n                    return;\n                }\n                final TagFeedResponse body = response.body();\n                if (body == null) {\n                    callback.onSuccess(null);\n                    return;\n                }\n                callback.onSuccess(new PostsFetchResponse(\n                        body.getItems(),\n                        body.getMoreAvailable(),\n                        body.getNextMaxId()\n                ));\n            }\n\n            @Override\n            public void onFailure(@NonNull final Call<TagFeedResponse> call, @NonNull final Throwable t) {\n                if (callback != null) {\n                    callback.onFailure(t);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/UserRepository.kt",
    "content": "package awais.instagrabber.webservices\n\nimport awais.instagrabber.repositories.UserService\nimport awais.instagrabber.repositories.responses.FriendshipStatus\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.UserSearchResponse\nimport awais.instagrabber.webservices.RetrofitFactory.retrofit\nimport java.util.*\n\nopen class UserRepository(private val service: UserService) {\n\n    suspend fun getUserInfo(uid: Long): User {\n        val response = service.getUserInfo(uid)\n        return response.user\n    }\n\n    open suspend fun getUsernameInfo(username: String): User {\n        val response = service.getUsernameInfo(username)\n        return response.user\n    }\n\n    open suspend fun getUserFriendship(uid: Long): FriendshipStatus = service.getUserFriendship(uid)\n\n    suspend fun search(query: String): UserSearchResponse {\n        val timezoneOffset = TimeZone.getDefault().rawOffset.toFloat() / 1000\n        return service.search(timezoneOffset, query)\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: UserRepository? = null\n\n        fun getInstance(): UserRepository {\n            return INSTANCE ?: synchronized(this) {\n                val service: UserService = retrofit.create(UserService::class.java)\n                UserRepository(service).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/interceptors/AddCookiesInterceptor.java",
    "content": "package awais.instagrabber.webservices.interceptors;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.IOException;\n\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.LocaleUtils;\nimport awais.instagrabber.utils.TextUtils;\nimport awais.instagrabber.utils.Utils;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic class AddCookiesInterceptor implements Interceptor {\n    @NonNull\n    @Override\n    public Response intercept(@NonNull final Chain chain) throws IOException {\n        final Request request = chain.request();\n        final Request.Builder builder = request.newBuilder();\n        final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);\n        final boolean hasCookie = !TextUtils.isEmpty(cookie);\n        if (hasCookie) {\n            builder.addHeader(\"Cookie\", cookie);\n        }\n        final String userAgentHeader = \"User-Agent\";\n        if (request.header(userAgentHeader) == null) {\n            builder.addHeader(userAgentHeader, Utils.settingsHelper.getString(hasCookie ? Constants.APP_UA : Constants.BROWSER_UA));\n        }\n        final String languageHeader = \"Accept-Language\";\n        if (request.header(languageHeader) == null) {\n            builder.addHeader(languageHeader, LocaleUtils.getCurrentLocale().getLanguage() + \",en-US;q=0.8\");\n        }\n        final Request updatedRequest = builder.build();\n        return chain.proceed(updatedRequest);\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/interceptors/IgErrorsInterceptor.java",
    "content": "package awais.instagrabber.webservices.interceptors;\n\nimport android.text.Html;\nimport android.text.Spanned;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.fragment.app.FragmentManager;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.activities.MainActivity;\nimport awais.instagrabber.dialogs.ConfirmDialogFragment;\nimport awais.instagrabber.utils.AppExecutors;\nimport awais.instagrabber.utils.Constants;\nimport awais.instagrabber.utils.TextUtils;\nimport okhttp3.Interceptor;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic class IgErrorsInterceptor implements Interceptor {\n    private static final String TAG = IgErrorsInterceptor.class.getSimpleName();\n\n    public IgErrorsInterceptor() { }\n\n    @NonNull\n    @Override\n    public Response intercept(@NonNull final Chain chain) throws IOException {\n        final Request request = chain.request();\n        final Response response = chain.proceed(request);\n        if (response.isSuccessful()) {\n            return response;\n        }\n        checkError(response);\n        return response;\n    }\n\n    private void checkError(@NonNull final Response response) {\n        final int errorCode = response.code();\n        switch (errorCode) {\n            case 429: // \"429 Too Many Requests\"\n                // ('Throttled by Instagram because of too many API requests.');\n                showErrorDialog(R.string.throttle_error);\n                return;\n            case 431: // \"431 Request Header Fields Too Large\"\n                // show dialog?\n                Log.e(TAG, \"Network error: \" + getMessage(errorCode, \"The request start-line and/or headers are too large to process.\"));\n                return;\n            case 404:\n                showErrorDialog(R.string.not_found);\n                return;\n            case 302: // redirect\n                final String location = response.header(\"location\");\n                if (location != null && location.equals(\"https://www.instagram.com/accounts/login/\")) {\n                    // rate limited\n                    final String message = MainActivity.getInstance().getString(R.string.rate_limit);\n                    final Spanned spanned = Html.fromHtml(message);\n                    showErrorDialog(spanned);\n                }\n                return;\n        }\n        final ResponseBody body = response.body();\n        if (body == null) return;\n        try {\n            final String bodyString = body.string();\n            Log.d(TAG, \"checkError: \" + bodyString);\n            JSONObject jsonObject = null;\n            try {\n                jsonObject = new JSONObject(bodyString);\n            } catch (JSONException e) {\n                Log.e(TAG, \"checkError: \", e);\n            }\n            String message;\n            if (jsonObject != null) {\n                message = jsonObject.optString(\"message\");\n            } else {\n                message = bodyString;\n            }\n            if (!TextUtils.isEmpty(message)) {\n                message = message.toLowerCase();\n                switch (message) {\n                    case \"user_has_logged_out\":\n                        showErrorDialog(R.string.account_logged_out);\n                        return;\n                    case \"login_required\":\n                        showErrorDialog(R.string.login_required);\n                        return;\n                    case \"execution failure\":\n                        showSnackbar(message);\n                        return;\n                    case \"not authorized to view user\": // Do we handle this in profile view fragment?\n                    case \"challenge_required\": // Since we make users login using browser, we should not be getting this error in api requests\n                    default:\n                        showSnackbar(message);\n                        Log.e(TAG, \"checkError: \" + bodyString);\n                        return;\n                }\n            }\n            final String errorType = jsonObject.optString(\"error_type\");\n            if (TextUtils.isEmpty(errorType)) return;\n            if (errorType.equals(\"sentry_block\")) {\n                showErrorDialog(\"\\\"sentry_block\\\". Please contact developers.\");\n                return;\n            }\n            if (errorType.equals(\"inactive user\")) {\n                showErrorDialog(R.string.inactive_user);\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"checkError: \", e);\n        }\n    }\n\n    private void showSnackbar(final String message) {\n        final MainActivity mainActivity = MainActivity.getInstance();\n        if (mainActivity == null) return;\n        // final View view = mainActivity.getRootView();\n        // if (view == null) return;\n        try {\n            AppExecutors.INSTANCE\n                    .getMainThread()\n                    .execute(() -> Toast.makeText(mainActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show());\n        } catch (Exception e) {\n            Log.e(TAG, \"showSnackbar: \", e);\n        }\n    }\n\n    @NonNull\n    private String getMessage(final int errorCode, final String message) {\n        return String.format(\"code: %s, internalMessage: %s\", errorCode, message);\n    }\n\n    private void showErrorDialog(@NonNull final CharSequence message) {\n        final MainActivity mainActivity = MainActivity.getInstance();\n        if (mainActivity == null) return;\n        final FragmentManager fragmentManager = mainActivity.getSupportFragmentManager();\n        if (fragmentManager.isStateSaved()) return;\n        final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(\n                Constants.GLOBAL_NETWORK_ERROR_DIALOG_REQUEST_CODE,\n                R.string.error,\n                message,\n                R.string.ok,\n                0,\n                0\n        );\n        dialogFragment.show(fragmentManager, \"network_error_dialog\");\n    }\n\n    private void showErrorDialog(@StringRes final int messageResId) {\n        showErrorDialog(MainActivity.getInstance().getString(messageResId));\n    }\n\n    public void destroy() {\n        // mainActivity = null;\n    }\n}"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/webservices/interceptors/LoggingInterceptor.java",
    "content": "package awais.instagrabber.webservices.interceptors;\n\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.IOException;\n\nimport okhttp3.Interceptor;\nimport okhttp3.MediaType;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport okhttp3.ResponseBody;\n\npublic class LoggingInterceptor implements Interceptor {\n    private static final String TAG = \"LoggingInterceptor\";\n\n    @NonNull\n    @Override\n    public Response intercept(Interceptor.Chain chain) throws IOException {\n        final Request request = chain.request();\n        long t1 = System.nanoTime();\n        Log.i(TAG, String.format(\"Sending request %s on %s%n%s\",\n                                 request.url(), chain.connection(), request.headers()));\n        final Response response = chain.proceed(request);\n        long t2 = System.nanoTime();\n        Log.i(TAG, String.format(\"Received response for %s in %.1fms%n%s\", response.request().url(), (t2 - t1) / 1e6d, response.headers()));\n        final ResponseBody body = response.body();\n        MediaType contentType = null;\n        String content = \"\";\n        if (body != null) {\n            contentType = body.contentType();\n            try {\n                content = body.string();\n            } catch (Exception e) {\n                Log.e(TAG, \"intercept: \", e);\n            }\n            Log.d(TAG, content);\n        }\n        final ResponseBody wrappedBody = ResponseBody.create(contentType, content);\n        return response.newBuilder()\n                       .body(wrappedBody)\n                       .build();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt",
    "content": "package awais.instagrabber.workers\n\nimport android.app.Notification\nimport android.app.PendingIntent\nimport android.content.ContentResolver\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.media.MediaMetadataRetriever\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Handler\nimport android.os.Looper\nimport android.util.Log\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.documentfile.provider.DocumentFile\nimport androidx.work.CoroutineWorker\nimport androidx.work.Data\nimport androidx.work.ForegroundInfo\nimport androidx.work.WorkerParameters\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.R\nimport awais.instagrabber.services.DeleteImageIntentService\nimport awais.instagrabber.utils.BitmapUtils\nimport awais.instagrabber.utils.Constants.DOWNLOAD_CHANNEL_ID\nimport awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME\nimport awais.instagrabber.utils.DownloadUtils\nimport awais.instagrabber.utils.TextUtils.isEmpty\nimport awais.instagrabber.utils.Utils\nimport awais.instagrabber.utils.extensions.TAG\nimport com.google.gson.Gson\nimport com.google.gson.JsonSyntaxException\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter\nimport java.io.BufferedInputStream\nimport java.io.File\nimport java.net.URL\nimport java.util.*\nimport java.util.concurrent.ExecutionException\nimport java.util.stream.Collectors\nimport kotlin.collections.Map\nimport kotlin.math.abs\n\nclass DownloadWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {\n    private val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(context)\n\n    override suspend fun doWork(): Result {\n        val downloadRequestFilePath = inputData.getString(KEY_DOWNLOAD_REQUEST_JSON)\n        if (downloadRequestFilePath.isNullOrBlank()) {\n            return Result.failure(Data.Builder()\n                .putString(\"error\", \"downloadRequest is empty or null\")\n                .build())\n        }\n        val downloadRequestString: String\n        val requestFile = Uri.parse(downloadRequestFilePath)\n        val context = applicationContext\n        val contentResolver = context.contentResolver ?: return Result.failure(Data.Builder()\n                .putString(\"error\", \"contentResolver is null\")\n                .build())\n        try {\n            val scanner = Scanner(contentResolver.openInputStream(requestFile))\n            downloadRequestString = scanner.useDelimiter(\"\\\\A\").next()\n        } catch (e: Exception) {\n            Log.e(TAG, \"doWork: \", e)\n            return Result.failure(Data.Builder()\n                .putString(\"error\", e.localizedMessage)\n                .build())\n        }\n        if (downloadRequestString.isBlank()) {\n            return Result.failure(Data.Builder()\n                .putString(\"error\", \"downloadRequest is empty\")\n                .build())\n        }\n        val downloadRequest: DownloadRequest = try {\n            Gson().fromJson(downloadRequestString, DownloadRequest::class.java)\n        } catch (e: JsonSyntaxException) {\n            Log.e(TAG, \"doWork\", e)\n            return Result.failure(Data.Builder()\n                .putString(\"error\", e.localizedMessage)\n                .build())\n        } ?: return Result.failure(Data.Builder()\n            .putString(\"error\", \"downloadRequest is null\")\n            .build())\n        val urlToFilePathMap = downloadRequest.urlToFilePathMap\n        download(urlToFilePathMap)\n        Handler(Looper.getMainLooper()).postDelayed({ showSummary(urlToFilePathMap) }, 500)\n        val deleted = DocumentFile.fromSingleUri(context, requestFile)!!.delete()\n        if (!deleted) {\n            Log.w(TAG, \"doWork: requestFile not deleted!\")\n        }\n        return Result.success()\n    }\n\n    private suspend fun download(urlToFilePathMap: Map<String, String>) {\n        val notificationId = notificationId\n        val entries = urlToFilePathMap.entries\n        var count = 1\n        val total = urlToFilePathMap.size\n        for ((url, uriString) in entries) {\n            updateDownloadProgress(notificationId, count, total, 0f)\n            withContext(Dispatchers.IO) {\n                val file = DocumentFile.fromSingleUri(applicationContext, Uri.parse(uriString))\n                download(notificationId, count, total, url, file!!)\n            }\n            count++\n        }\n    }\n\n    private val notificationId: Int\n        get() = abs(id.hashCode())\n\n    private fun download(\n        notificationId: Int,\n        position: Int,\n        total: Int,\n        url: String,\n        filePath: DocumentFile,\n    ) {\n        val context = applicationContext.let { it }\n        val contentResolver = context.contentResolver?.let { it } ?: return\n        val filePathType = filePath.type?.let { it } ?: return\n        val isJpg = filePathType.startsWith(\"image\")\n        // using temp file approach to remove IPTC so that download progress can be reported\n        val outFile = if (isJpg) DownloadUtils.getTempFile(null, \"jpg\") else filePath\n        try {\n            val urlConnection = URL(url).openConnection()\n            val fileSize = if (Build.VERSION.SDK_INT >= 24) urlConnection.contentLengthLong else urlConnection.contentLength.toLong()\n            var totalRead = 0f\n            try {\n                BufferedInputStream(urlConnection.getInputStream()).use { bis ->\n                    contentResolver.openOutputStream(outFile!!.uri).use { fos ->\n                        val buffer = ByteArray(0x2000)\n                        var count: Int\n                        while (bis.read(buffer, 0, 0x2000).also { count = it } != -1) {\n                            totalRead += count\n                            fos!!.write(buffer, 0, count)\n                            setProgressAsync(Data.Builder().putString(URL, url)\n                                .putFloat(PROGRESS, totalRead * 100f / fileSize)\n                                .build())\n                            updateDownloadProgress(notificationId, position, total, totalRead * 100f / fileSize)\n                        }\n                        fos!!.flush()\n                    }\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"Error while writing data from url: \" + url + \" to file: \" + outFile!!.name, e)\n            }\n            if (isJpg) {\n                try {\n                    contentResolver.openInputStream(outFile!!.uri).use { fis ->\n                        contentResolver.openOutputStream(filePath.uri).use { fos ->\n                            val jpegIptcRewriter = JpegIptcRewriter()\n                            jpegIptcRewriter.removeIPTC(fis, fos)\n                        }\n                    }\n                } catch (e: Exception) {\n                    Log.e(TAG, \"Error while removing iptc: url: \" + url\n                               + \", tempFile: \" + outFile!!.name\n                               + \", finalFile: \" + filePath.name, e)\n                }\n                val deleted = outFile!!.delete()\n                if (!deleted) {\n                    Log.w(TAG, \"download: tempFile not deleted!\")\n                }\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Error while downloading: $url\", e)\n        }\n        setProgressAsync(Data.Builder().putString(URL, url)\n            .putFloat(PROGRESS, 100f)\n            .build())\n        updateDownloadProgress(notificationId, position, total, 100f)\n    }\n\n    private fun updateDownloadProgress(\n        notificationId: Int,\n        position: Int,\n        total: Int,\n        percent: Float,\n    ) {\n        val notification = createProgressNotification(position, total, percent)\n        try {\n            if (notification == null) {\n                notificationManager.cancel(notificationId)\n                return\n            }\n            setForegroundAsync(ForegroundInfo(notificationId, notification)).get()\n        } catch (e: ExecutionException) {\n            Log.e(TAG, \"updateDownloadProgress\", e)\n        } catch (e: InterruptedException) {\n            Log.e(TAG, \"updateDownloadProgress\", e)\n        }\n    }\n\n    private fun createProgressNotification(position: Int, total: Int, percent: Float): Notification? {\n        val context = applicationContext\n        var ongoing = true\n        val totalPercent: Int\n        if (position == total && percent == 100f) {\n            ongoing = false\n            totalPercent = 100\n        } else {\n            totalPercent = (100f * (position - 1) / total + 1f / total * percent).toInt()\n        }\n        if (totalPercent == 100) {\n            return null\n        }\n        // Log.d(TAG, \"createProgressNotification: position: \" + position\n        //         + \", total: \" + total\n        //         + \", percent: \" + percent\n        //         + \", totalPercent: \" + totalPercent);\n        val builder = NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)\n            .setCategory(NotificationCompat.CATEGORY_PROGRESS)\n            .setSmallIcon(R.drawable.ic_download)\n            .setOngoing(ongoing)\n            .setProgress(100, totalPercent, totalPercent < 0)\n            .setAutoCancel(false)\n            .setOnlyAlertOnce(true)\n            .setContentTitle(context.getString(R.string.downloader_downloading_post))\n        if (total > 1) {\n            builder.setContentText(context.getString(R.string.downloader_downloading_child, position, total))\n        }\n        return builder.build()\n    }\n\n    private fun showSummary(urlToFilePathMap: Map<String, String>) {\n        val context = applicationContext\n        val filePaths = urlToFilePathMap.mapNotNull { DocumentFile.fromSingleUri(context, Uri.parse(it.value)) }\n        val notifications: MutableList<NotificationCompat.Builder> = LinkedList()\n        val notificationIds: MutableList<Int> = LinkedList()\n        var count = 1\n        for (filePath: DocumentFile in filePaths) {\n            // final File file = new File(filePath);\n            // context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, filePath.getUri()));\n            // Utils.scanDocumentFile(context, filePath, (path, uri) -> {});\n            // final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + \".provider\", file);\n            val contentResolver = context.contentResolver\n            var bitmap: Bitmap? = null\n            val mimeType = filePath.type // Utils.getMimeType(uri, contentResolver);\n            if (!isEmpty(mimeType)) {\n                if (mimeType!!.startsWith(\"image\")) {\n                    try {\n                        contentResolver.openInputStream(filePath.uri).use { inputStream ->\n                            bitmap = BitmapFactory.decodeStream(inputStream)\n                        }\n                    } catch (e: java.lang.Exception) {\n                        if (BuildConfig.DEBUG) Log.e(TAG, \"\", e)\n                    }\n                } else if (mimeType.startsWith(\"video\")) {\n                    val retriever = MediaMetadataRetriever()\n                    try {\n                        try {\n                            retriever.setDataSource(context, filePath.uri)\n                        } catch (e: java.lang.Exception) {\n                            // retriever.setDataSource(file.getAbsolutePath());\n                            Log.e(TAG, \"showSummary: \", e)\n                        }\n                        bitmap = retriever.frameAtTime\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) try {\n                            retriever.close()\n                        } catch (e: java.lang.Exception) {\n                            Log.e(TAG, \"showSummary: \", e)\n                        }\n                    } catch (e: java.lang.Exception) {\n                        Log.e(TAG, \"\", e)\n                    }\n                }\n            }\n            val downloadComplete = context.getString(R.string.downloader_complete)\n            val intent = Intent(Intent.ACTION_VIEW, filePath.uri)\n                .addFlags(\n                    Intent.FLAG_ACTIVITY_NEW_TASK\n                            or Intent.FLAG_FROM_BACKGROUND\n                            or Intent.FLAG_GRANT_READ_URI_PERMISSION\n                            or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n                )\n                .putExtra(Intent.EXTRA_STREAM, filePath.uri)\n            val pendingIntent = PendingIntent.getActivity(\n                context,\n                DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,\n                intent,\n                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_ONE_SHOT\n            )\n            val notificationId: Int = notificationId + count\n            notificationIds.add(notificationId)\n            count++\n            val builder: NotificationCompat.Builder =\n                NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)\n                    .setSmallIcon(R.drawable.ic_download)\n                    .setContentText(null)\n                    .setContentTitle(downloadComplete)\n                    .setWhen(System.currentTimeMillis())\n                    .setOnlyAlertOnce(true)\n                    .setAutoCancel(true)\n                    .setGroup(NOTIF_GROUP_NAME + \"_\" + id)\n                    .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)\n                    .setContentIntent(pendingIntent)\n                    .addAction(\n                        R.drawable.ic_delete,\n                        context.getString(R.string.delete),\n                        DeleteImageIntentService.pendingIntent(context, filePath, notificationId)\n                    )\n            if (bitmap != null) {\n                builder.setLargeIcon(bitmap)\n                    .setStyle(\n                        NotificationCompat.BigPictureStyle()\n                            .bigPicture(bitmap)\n                            .bigLargeIcon(null)\n                    )\n                    .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)\n            }\n            notifications.add(builder)\n        }\n        var summaryNotification: Notification? = null\n        if (urlToFilePathMap.size != 1) {\n            val text = \"Downloaded \" + urlToFilePathMap.size + \" items\"\n            summaryNotification = NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)\n                .setContentTitle(\"Downloaded\")\n                .setContentText(text)\n                .setSmallIcon(R.drawable.ic_download)\n                .setStyle(NotificationCompat.InboxStyle().setSummaryText(text))\n                .setGroup(NOTIF_GROUP_NAME + \"_\" + id)\n                .setGroupSummary(true)\n                .build()\n        }\n        for (i in notifications.indices) {\n            val builder = notifications[i]\n            // only make sound and vibrate for the last notification\n            if (i != notifications.size - 1) {\n                builder.setSound(null)\n                    .setVibrate(null)\n            }\n            notificationManager.notify(notificationIds[i], builder.build())\n        }\n        if (summaryNotification != null) {\n            notificationManager.notify(notificationId + count, summaryNotification)\n        }\n    }\n\n    private fun getThumbnail(\n        context: Context,\n        file: File,\n        uri: Uri,\n        contentResolver: ContentResolver,\n    ): Bitmap? {\n        val mimeType = Utils.getMimeType(uri, contentResolver)\n        if (isEmpty(mimeType)) return null\n        var bitmap: Bitmap? = null\n        if (mimeType.startsWith(\"image\")) {\n            try {\n                val bitmapResult = BitmapUtils.getBitmapResult(\n                    context.contentResolver,\n                    uri,\n                    BitmapUtils.THUMBNAIL_SIZE,\n                    BitmapUtils.THUMBNAIL_SIZE,\n                    -1f,\n                    true\n                ) ?: return null\n                bitmap = bitmapResult.bitmap\n            } catch (e: Exception) {\n                Log.e(TAG, \"\", e)\n            }\n            return bitmap\n        }\n        if (mimeType.startsWith(\"video\")) {\n            try {\n                val retriever = MediaMetadataRetriever()\n                bitmap = try {\n                    try {\n                        retriever.setDataSource(context, uri)\n                    } catch (e: Exception) {\n                        retriever.setDataSource(file.absolutePath)\n                    }\n                    retriever.frameAtTime\n                } finally {\n                    try {\n                        retriever.release()\n                    } catch (e: Exception) {\n                        Log.e(TAG, \"getThumbnail: \", e)\n                    }\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"\", e)\n            }\n        }\n        return bitmap\n    }\n\n    class DownloadRequest private constructor(val urlToFilePathMap: Map<String, String>) {\n\n        class Builder {\n            private var urlToFilePathMap: MutableMap<String, String> = mutableMapOf()\n            fun setUrlToFilePathMap(urlToFilePathMap: Map<String, DocumentFile?>): Builder {\n                this.urlToFilePathMap = urlToFilePathMap\n                    .filter{ it.value != null }\n                    .mapValues { it.value!!.uri.toString() }\n                    .toMutableMap()\n                return this\n            }\n\n            fun addUrl(url: String, filePath: DocumentFile): Builder {\n                urlToFilePathMap[url] = filePath.uri.toString()\n                return this\n            }\n\n            fun build(): DownloadRequest {\n                return DownloadRequest(urlToFilePathMap)\n            }\n        }\n\n        companion object {\n            @JvmStatic\n            fun builder(): Builder {\n                return Builder()\n            }\n        }\n    }\n\n    companion object {\n        const val PROGRESS = \"PROGRESS\"\n        const val URL = \"URL\"\n        const val KEY_DOWNLOAD_REQUEST_JSON = \"download_request_json\"\n        private const val DOWNLOAD_GROUP = \"DOWNLOAD_GROUP\"\n        private const val DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE = 2020\n        private const val DELETE_IMAGE_REQUEST_CODE = 2030\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/awaisomereport/CrashReporter.kt",
    "content": "package awaisomereport\n\nimport android.app.Application\n\nclass CrashReporter private constructor(application: Application) : Thread.UncaughtExceptionHandler {\n\n    private val crashHandler: CrashHandler?\n    private var startAttempted = false\n    private var defaultExceptionHandler: Thread.UncaughtExceptionHandler? = null\n\n    init {\n        crashHandler = CrashHandler(application)\n    }\n\n    fun start() {\n        if (startAttempted) return\n        startAttempted = true\n        defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()\n        Thread.setDefaultUncaughtExceptionHandler(this)\n    }\n\n    override fun uncaughtException(t: Thread, exception: Throwable) {\n        if (crashHandler == null) {\n            defaultExceptionHandler?.uncaughtException(t, exception)\n            return\n        }\n        crashHandler.uncaughtException(t, exception, defaultExceptionHandler ?: return)\n    }\n\n    companion object {\n        @Volatile\n        private var INSTANCE: CrashReporter? = null\n\n        fun getInstance(application: Application): CrashReporter {\n            return INSTANCE ?: synchronized(this) {\n                CrashReporter(application).also { INSTANCE = it }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awaisomereport/CrashReporterHelper.kt",
    "content": "package awaisomereport\n\nimport android.app.Application\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.util.Log\nimport awais.instagrabber.BuildConfig\nimport awais.instagrabber.R\nimport awais.instagrabber.utils.Constants\nimport awais.instagrabber.utils.extensions.TAG\nimport java.io.*\nimport java.time.LocalDateTime\n\nobject CrashReporterHelper {\n    private val shortBorder = \"=\".repeat(14)\n    private val longBorder = \"=\".repeat(21)\n    private const val prefix = \"stack-\"\n    private const val suffix = \".stacktrace\"\n\n    fun startErrorReporterActivity(\n        application: Application,\n        exception: Throwable\n    ) {\n        try {\n            application.openFileOutput(\n                \"$prefix${System.currentTimeMillis()}$suffix\",\n                Context.MODE_PRIVATE\n            ).use { it.write(getReportContent(exception).toByteArray()) }\n        } catch (ex: Exception) {\n            if (BuildConfig.DEBUG) Log.e(TAG, \"\", ex)\n        }\n        application.startActivity(Intent(application, ErrorReporterActivity::class.java).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))\n    }\n\n    fun getReportContent(exception: Throwable): String {\n        var reportContent =\n            \"\"\"\n                IMPORTANT: If sending by email, your email address and the entire content will be made public at https://github.com/austinhuang0131/barinsta/issues.\n\n                When possible, please describe the steps leading to this crash. Thank you for your cooperation.\n\n                Error report collected on: ${LocalDateTime.now()}\n\n                Information:\n                $shortBorder\n                VERSION\t\t    : ${BuildConfig.VERSION_NAME}\n                VERSION_CODE\t: ${BuildConfig.VERSION_CODE}\n                PHONE-MODEL\t    : ${Build.MODEL}\n                ANDROID_VERS\t: ${Build.VERSION.RELEASE}\n                ANDROID_REL\t    : ${Build.VERSION.SDK_INT}\n                BRAND\t\t\t: ${Build.BRAND}\n                MANUFACTURER\t: ${Build.MANUFACTURER}\n                BOARD\t\t\t: ${Build.BOARD}\n                DEVICE\t\t\t: ${Build.DEVICE}\n                PRODUCT\t\t    : ${Build.PRODUCT}\n                HOST\t\t\t: ${Build.HOST}\n                TAGS\t\t\t: ${Build.TAGS}\n\n                Stack:\n                $shortBorder\n            \"\"\".trimIndent()\n        reportContent = \"$reportContent${getStackStrace(exception)}\\n\\n*** End of current Report ***\"\n        return reportContent.replace(\"\\n\", \"\\r\\n\")\n    }\n\n    private fun getStackStrace(exception: Throwable): String {\n        val writer = StringWriter()\n        return PrintWriter(writer).use {\n            val reportBuilder = StringBuilder(\"\\n\")\n            exception.printStackTrace(it)\n            reportBuilder.append(writer.toString())\n            var cause = exception.cause\n            if (cause != null) reportBuilder.append(\"\\nCause:\\n$shortBorder\\n\")\n            while (cause != null) {\n                cause.printStackTrace(it)\n                reportBuilder.append(writer.toString())\n                cause = cause.cause\n            }\n            return@use reportBuilder.toString()\n        }\n    }\n\n    @JvmStatic\n    fun startCrashEmailIntent(context: Context) {\n        try {\n            val filePath = context.filesDir.absolutePath\n            val errorFileList: Array<String>? = try {\n                val dir = File(filePath)\n                if (dir.exists() && !dir.isDirectory) {\n                    dir.delete()\n                }\n                dir.mkdirs()\n                dir.list { _: File?, name: String -> name.endsWith(suffix) }\n            } catch (e: Exception) {\n                null\n            }\n            if (errorFileList == null || errorFileList.isEmpty()) {\n                return\n            }\n            val errorStringBuilder: StringBuilder = StringBuilder(\"\\n\\n\")\n            val maxSendMail = 5\n            for ((curIndex, curString) in errorFileList.withIndex()) {\n                val file = File(\"$filePath/$curString\")\n                if (curIndex <= maxSendMail) {\n                    errorStringBuilder.append(\"New Trace collected:\\n$longBorder\\n\")\n                    BufferedReader(FileReader(file)).use { input ->\n                        var line: String?\n                        while (input.readLine().also { line = it } != null) errorStringBuilder.append(line).append(\"\\n\")\n                    }\n                }\n                file.delete()\n            }\n            errorStringBuilder.append(\"\\n\\n\")\n            val resources = context.resources\n            context.startActivity(\n                Intent.createChooser(\n                    Intent(Intent.ACTION_SEND).apply {\n                        type = \"message/rfc822\"\n                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n                        putExtra(Intent.EXTRA_EMAIL, arrayOf(Constants.CRASH_REPORT_EMAIL))\n                        putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.crash_report_subject))\n                        putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString().replace(\"\\n\", \"\\r\\n\"))\n                    },\n                    context.resources.getString(R.string.crash_report_title)\n                )\n            )\n        } catch (e: Exception) {\n            Log.e(TAG, \"\", e)\n        }\n    }\n\n    @JvmStatic\n    fun deleteAllStacktraceFiles(context: Context) {\n        val filePath = context.filesDir.absolutePath\n        val errorFileList: Array<File>? = try {\n            val dir = File(filePath)\n            if (dir.exists() && !dir.isDirectory) {\n                dir.delete()\n            }\n            dir.mkdirs()\n            dir.listFiles { _: File?, name: String -> name.endsWith(suffix) }\n        } catch (e: Exception) {\n            null\n        }\n        errorFileList?.forEach { it.delete() }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awaisomereport/ErrorReporterActivity.kt",
    "content": "package awaisomereport\n\nimport android.app.Activity\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Paint\nimport android.graphics.Paint.FontMetricsInt\nimport android.graphics.drawable.Drawable\nimport android.os.Bundle\nimport android.text.Spannable\nimport android.text.SpannableString\nimport android.text.style.ImageSpan\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.annotation.DrawableRes\nimport awais.instagrabber.R\nimport awais.instagrabber.databinding.ActivityCrashErrorBinding\nimport awaisomereport.CrashReporterHelper.startCrashEmailIntent\nimport java.lang.ref.WeakReference\nimport kotlin.system.exitProcess\n\nclass ErrorReporterActivity : Activity(), View.OnClickListener {\n\n    private lateinit var binding: ActivityCrashErrorBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityCrashErrorBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n        setFinishOnTouchOutside(false)\n        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)\n        val crashTitle = SpannableString(\"   \" + getString(R.string.crash_title))\n        crashTitle.setSpan(\n            CenteredImageSpan(this, android.R.drawable.stat_notify_error),\n            0,\n            1,\n            Spannable.SPAN_INCLUSIVE_EXCLUSIVE\n        )\n        title = crashTitle\n        binding.btnReport.setOnClickListener(this)\n        binding.btnCancel.setOnClickListener(this)\n    }\n\n    override fun onClick(v: View) {\n        if (v === binding.btnReport) {\n            startCrashEmailIntent(this)\n        }\n        finish()\n        exitProcess(10)\n    }\n\n    private class CenteredImageSpan(context: Context, @DrawableRes drawableRes: Int) : ImageSpan(context, drawableRes) {\n\n        private var drawable: WeakReference<Drawable>? = null\n\n        override fun getSize(\n            paint: Paint,\n            text: CharSequence,\n            start: Int,\n            end: Int,\n            fm: FontMetricsInt?\n        ): Int {\n            fm?.apply {\n                val pfm = paint.fontMetricsInt\n                ascent = pfm.ascent\n                descent = pfm.descent\n                top = pfm.top\n                bottom = pfm.bottom\n            }\n            return cachedDrawable.bounds.right\n        }\n\n        override fun draw(\n            canvas: Canvas,\n            text: CharSequence,\n            start: Int,\n            end: Int,\n            x: Float,\n            top: Int,\n            y: Int,\n            bottom: Int,\n            paint: Paint\n        ) {\n            canvas.save()\n            val drawableHeight = cachedDrawable.intrinsicHeight\n            val fontMetricsInt = paint.fontMetricsInt\n            val transY = bottom - cachedDrawable.bounds.bottom + (drawableHeight - fontMetricsInt.descent + fontMetricsInt.ascent) / 2\n            canvas.translate(x, transY.toFloat())\n            cachedDrawable.draw(canvas)\n            canvas.restore()\n        }\n\n        private val cachedDrawable: Drawable\n            get() = drawable?.get() ?: getDrawable().also { drawable = WeakReference(it) }\n    }\n}"
  },
  {
    "path": "app/src/main/java/awaisomereport/ICrashHandler.kt",
    "content": "package awaisomereport\n\ninterface ICrashHandler {\n    fun uncaughtException(\n        t: Thread,\n        exception: Throwable,\n        defaultExceptionHandler: Thread.UncaughtExceptionHandler\n    )\n}"
  },
  {
    "path": "app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java",
    "content": "package thoughtbot.expandableadapter;\n\nimport java.util.List;\n\nimport awais.instagrabber.repositories.responses.User;\n\npublic class ExpandableGroup {\n    private final String title;\n    private final List<User> items;\n\n    public ExpandableGroup(final String title, final List<User> items) {\n        this.title = title;\n        this.items = items;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public List<User> getItems() {\n        return items;\n    }\n\n    public int getItemCount() {\n        if (items != null) {\n            return items.size();\n        }\n        return 0;\n    }\n}"
  },
  {
    "path": "app/src/main/java/thoughtbot/expandableadapter/ExpandableList.java",
    "content": "package thoughtbot.expandableadapter;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.util.ArrayList;\n\npublic final class ExpandableList {\n    private final int groupsSize;\n    public final ArrayList<ExpandableGroup> groups;\n    public final boolean[] expandedGroupIndexes;\n\n    public ExpandableList(@NonNull final ArrayList<ExpandableGroup> groups) {\n        this.groups = groups;\n        this.groupsSize = groups.size();\n        this.expandedGroupIndexes = new boolean[groupsSize];\n    }\n\n    public ExpandableList(@NonNull final ArrayList<ExpandableGroup> groups,\n                          @Nullable final boolean[] expandedGroupIndexes) {\n        this.groups = groups;\n        this.groupsSize = groups.size();\n        this.expandedGroupIndexes = expandedGroupIndexes;\n    }\n\n    public int getVisibleItemCount() {\n        int count = 0;\n        for (int i = 0; i < groupsSize; i++) count = count + numberOfVisibleItemsInGroup(i);\n        return count;\n    }\n\n    @NonNull\n    public ExpandableListPosition getUnflattenedPosition(final int flPos) {\n        int adapted = flPos;\n        for (int i = 0; i < groupsSize; i++) {\n            final int groupItemCount = numberOfVisibleItemsInGroup(i);\n            if (adapted == 0)\n                return ExpandableListPosition.obtain(ExpandableListPosition.GROUP, i, -1, flPos);\n            else if (adapted < groupItemCount)\n                return ExpandableListPosition.obtain(ExpandableListPosition.CHILD, i, adapted - 1, flPos);\n            adapted = adapted - groupItemCount;\n        }\n        throw new RuntimeException(\"Unknown state\");\n    }\n\n    private int numberOfVisibleItemsInGroup(final int group) {\n        return expandedGroupIndexes[group] ? groups.get(group).getItemCount() + 1 : 1;\n    }\n\n    public int getFlattenedGroupIndex(@NonNull final ExpandableListPosition listPosition) {\n        int runningTotal = 0;\n        for (int i = 0; i < listPosition.groupPos; i++) runningTotal = runningTotal + numberOfVisibleItemsInGroup(i);\n        return runningTotal;\n    }\n\n    public ExpandableGroup getExpandableGroup(@NonNull ExpandableListPosition listPosition) {\n        return groups.get(listPosition.groupPos);\n    }\n}"
  },
  {
    "path": "app/src/main/java/thoughtbot/expandableadapter/ExpandableListPosition.java",
    "content": "package thoughtbot.expandableadapter;\n\nimport androidx.annotation.NonNull;\n\npublic class ExpandableListPosition {\n    private static final ExpandableListPosition LIST_POSITION = new ExpandableListPosition();\n    public final static int CHILD = 1;\n    public final static int GROUP = 2;\n    private int flatListPos;\n    public int groupPos;\n    public int childPos;\n    public int type;\n\n    @NonNull\n    public static ExpandableListPosition obtain(final int type, final int groupPos, final int childPos, final int flatListPos) {\n        LIST_POSITION.type = type;\n        LIST_POSITION.groupPos = groupPos;\n        LIST_POSITION.childPos = childPos;\n        LIST_POSITION.flatListPos = flatListPos;\n        return LIST_POSITION;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) return true;\n        //if (o != null && getClass() == o.getClass()) {\n        if (o instanceof ExpandableListPosition) {\n            final ExpandableListPosition that = (ExpandableListPosition) o;\n            if (groupPos != that.groupPos) return false;\n            if (childPos != that.childPos) return false;\n            if (flatListPos != that.flatListPos) return false;\n            return type == that.type;\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return 31 * (31 * (31 * groupPos + childPos) + flatListPos) + type;\n    }\n}"
  },
  {
    "path": "app/src/main/java/thoughtbot/expandableadapter/GroupViewHolder.java",
    "content": "package thoughtbot.expandableadapter;\n\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport awais.instagrabber.R;\nimport awais.instagrabber.interfaces.OnGroupClickListener;\n\npublic class GroupViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {\n    private final OnGroupClickListener listener;\n    private final TextView title;\n    private final ImageView arrow;\n\n    public GroupViewHolder(@NonNull final View itemView, final OnGroupClickListener listener) {\n        super(itemView);\n        this.listener = listener;\n        this.title = itemView.findViewById(android.R.id.text1);\n        this.arrow = itemView.findViewById(R.id.collapsingArrow);\n        this.title.setBackgroundColor(0x80_1565C0);\n        itemView.setOnClickListener(this);\n    }\n\n    public void setTitle(@NonNull final String title) {\n        this.title.setText(title);\n    }\n\n    @Override\n    public void onClick(final View v) {\n        if (listener != null) listener.toggleGroup(getLayoutPosition());\n    }\n\n    public void toggle(final boolean expand) {\n        arrow.setImageResource(expand ? R.drawable.ic_keyboard_arrow_up_24 : R.drawable.ic_keyboard_arrow_down_24);\n    }\n}"
  },
  {
    "path": "app/src/main/res/anim/dialog_anim_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <scale\n        android:duration=\"400\"\n        android:fillAfter=\"false\"\n        android:fromXScale=\"0.9\"\n        android:fromYScale=\"0.9\"\n        android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"30%\"\n        android:toXScale=\"1.0\"\n        android:toYScale=\"1.0\" />\n\n    <alpha\n        android:duration=\"200\"\n        android:fromAlpha=\"0.5\"\n        android:toAlpha=\"1.0\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/dialog_anim_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <scale\n        android:duration=\"400\"\n        android:fillAfter=\"false\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"30%\"\n        android:toXScale=\"0.9\"\n        android:toYScale=\"0.9\" />\n\n    <alpha\n        android:duration=\"200\"\n        android:startOffset=\"200\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0.0\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/slide_in_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:fromXDelta=\"50%p\"\n        android:toXDelta=\"0\" />\n    <alpha\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:fromAlpha=\"0.0\"\n        android:toAlpha=\"1.0\" />\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"300\"\n    android:fromXDelta=\"100%\"\n    android:toXDelta=\"0%\" />"
  },
  {
    "path": "app/src/main/res/anim/slide_out_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:fromXDelta=\"0\"\n        android:toXDelta=\"-50%p\" />\n    <alpha\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0.0\" />\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"300\"\n    android:fromXDelta=\"0%\"\n    android:toXDelta=\"100%\" />"
  },
  {
    "path": "app/src/main/res/animator/basket_path.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:duration=\"500\"\n        android:propertyName=\"pathData\"\n        android:valueFrom=\"M 6 19 C 6 20.1 6.9 21 8 21 L 16 21 C 17.1 21 18 20.1 18 19 L 18 7 L 6 7 L 6 19 Z M 19 4 L 15.5 4 L 14.5 3 L 9.5 3 L 8.5 4 L 5 4 L 5 6 L 19 6 L 19 4 Z\"\n        android:valueTo=\"M 6 19 C 6 20.1 6.9 21 8 21 L 16 21 C 17.1 21 18 20.1 18 19 L 18 7 L 6 7 L 6 19 Z M 5.442 2 L 4.332 5.319 L 3.066 5.95 L 1.48 10.692 L 2.111 11.958 L 1 15.277 L 2.897 15.912 L 7.338 2.635 L 5.442 2 Z\"\n        android:valueType=\"pathType\" />\n    <objectAnimator\n        android:duration=\"500\"\n        android:propertyName=\"pathData\"\n        android:valueFrom=\"M 6 19 C 6 20.1 6.9 21 8 21 L 16 21 C 17.1 21 18 20.1 18 19 L 18 7 L 6 7 L 6 19 Z M 5.442 2 L 4.332 5.319 L 3.066 5.95 L 1.48 10.692 L 2.111 11.958 L 1 15.277 L 2.897 15.912 L 7.338 2.635 L 5.442 2 Z\"\n        android:valueTo=\"M 6 19 C 6 20.1 6.9 21 8 21 L 16 21 C 17.1 21 18 20.1 18 19 L 18 7 L 6 7 L 6 19 Z M 19 4 L 15.5 4 L 14.5 3 L 9.5 3 L 8.5 4 L 5 4 L 5 6 L 19 6 L 19 4 Z\"\n        android:valueType=\"pathType\" />\n</set>"
  },
  {
    "path": "app/src/main/res/animator/delete_mic_animation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:anim/decelerate_interpolator\"\n    android:ordering=\"sequentially\">\n\n    <set\n        android:ordering=\"together\"\n        android:repeatMode=\"reverse\">\n\n        <objectAnimator\n            android:duration=\"500\"\n            android:propertyName=\"translationY\"\n            android:repeatMode=\"reverse\"\n            android:valueFrom=\"0\"\n            android:valueTo=\"-400\"\n            android:valueType=\"floatType\" />\n\n        <set\n            android:duration=\"500\"\n            android:ordering=\"together\">\n            <objectAnimator\n\n                android:propertyName=\"scaleX\"\n                android:valueFrom=\"1.0\"\n                android:valueTo=\"2.0\"\n                android:valueType=\"floatType\" />\n\n            <objectAnimator\n                android:propertyName=\"scaleY\"\n                android:valueFrom=\"1.0\"\n                android:valueTo=\"2.0\"\n                android:valueType=\"floatType\" />\n        </set>\n\n        <objectAnimator\n            android:duration=\"400\"\n            android:propertyName=\"rotation\"\n            android:valueFrom=\"0\"\n            android:valueTo=\"360\"\n            android:valueType=\"floatType\" />\n    </set>\n\n    <set\n        android:duration=\"500\"\n        android:ordering=\"together\">\n\n        <objectAnimator\n            android:propertyName=\"translationY\"\n            android:valueFrom=\"-400\"\n            android:valueTo=\"0\"\n            android:valueType=\"floatType\" />\n\n        <set android:ordering=\"together\">\n            <objectAnimator\n                android:propertyName=\"scaleX\"\n                android:valueFrom=\"2.0\"\n                android:valueTo=\"0.7\"\n                android:valueType=\"floatType\" />\n            <objectAnimator\n                android:propertyName=\"scaleY\"\n                android:valueFrom=\"2.0\"\n                android:valueTo=\"0.7\"\n                android:valueType=\"floatType\" />\n        </set>\n    </set>\n</set>"
  },
  {
    "path": "app/src/main/res/color/emoji_picker_tab_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"?android:attr/textColorPrimary\" android:state_selected=\"false\" />\n    <item android:color=\"@color/blue_500\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/filter_name_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/white\" android:state_selected=\"true\" />\n    <item android:color=\"@color/grey_600\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/ic_circle_check_tint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/blue_400\" android:state_selected=\"true\" />\n    <item android:color=\"?colorControlNormal\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/ic_read_button_tint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/grey_600_a20\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorControlNormal\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/image_edit_tab_tint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/blue_400\" android:state_selected=\"true\" />\n    <item android:color=\"@color/white\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/avd_mic_to_send_anim.xml",
    "content": "<animated-vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\">\n    <aapt:attr name=\"android:drawable\">\n        <vector\n            android:name=\"vector\"\n            android:width=\"24dp\"\n            android:height=\"24dp\"\n            android:viewportWidth=\"24\"\n            android:viewportHeight=\"24\">\n            <group\n                android:name=\"mic\"\n                android:pivotX=\"12\"\n                android:pivotY=\"12\">\n                <path\n                    android:name=\"path\"\n                    android:pathData=\"M 12 15 C 13.66 15 14.99 13.66 14.99 12 L 15 6 C 15 4.34 13.66 3 12 3 C 10.34 3 9 4.34 9 6 L 9 12 C 9 13.66 10.34 15 12 15 Z M 10.8 5.9 C 10.8 5.24 11.34 4.7 12 4.7 C 12.66 4.7 13.2 5.24 13.2 5.9 L 13.19 12.1 C 13.19 12.76 12.66 13.3 12 13.3 C 11.34 13.3 10.8 12.76 10.8 12.1 L 10.8 5.9 Z M 17.3 12 C 17.3 15 14.76 17.1 12 17.1 C 9.24 17.1 6.7 15 6.7 12 L 5 12 C 5 15.41 7.72 18.23 11 18.72 L 11 22 L 13 22 L 13 18.72 C 16.28 18.24 19 15.42 19 12 L 17.3 12 Z\"\n                    android:fillColor=\"#000000\"\n                    android:strokeAlpha=\"0\"/>\n            </group>\n            <group\n                android:name=\"send\"\n                android:pivotX=\"12\"\n                android:pivotY=\"12\"\n                android:translateX=\"2\"\n                android:scaleX=\"0.5\"\n                android:scaleY=\"0.5\">\n                <path\n                    android:name=\"path_1\"\n                    android:pathData=\"M 3.4 20.4 L 20.85 12.92 C 21.66 12.57 21.66 11.43 20.85 11.08 L 3.4 3.6 C 2.74 3.31 2.01 3.8 2.01 4.51 L 2 9.12 C 2 9.62 2.37 10.05 2.87 10.11 L 17 12 L 2.87 13.88 C 2.37 13.95 2 14.38 2 14.88 L 2.01 19.49 C 2.01 20.2 2.74 20.69 3.4 20.4 Z\"\n                    android:fillColor=\"#000000\"\n                    android:fillAlpha=\"0\"\n                    android:strokeAlpha=\"0\"/>\n            </group>\n        </vector>\n    </aapt:attr>\n    <target android:name=\"mic\">\n        <aapt:attr name=\"android:animation\">\n            <set>\n                <objectAnimator\n                    android:propertyName=\"scaleX\"\n                    android:duration=\"100\"\n                    android:valueFrom=\"1\"\n                    android:valueTo=\"0.5\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n                <objectAnimator\n                    android:propertyName=\"scaleY\"\n                    android:duration=\"100\"\n                    android:valueFrom=\"1\"\n                    android:valueTo=\"0.5\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n            </set>\n        </aapt:attr>\n    </target>\n    <target android:name=\"send\">\n        <aapt:attr name=\"android:animation\">\n            <set>\n                <objectAnimator\n                    android:propertyName=\"scaleX\"\n                    android:startOffset=\"90\"\n                    android:duration=\"110\"\n                    android:valueFrom=\"0.5\"\n                    android:valueTo=\"1\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n                <objectAnimator\n                    android:propertyName=\"scaleY\"\n                    android:startOffset=\"90\"\n                    android:duration=\"110\"\n                    android:valueFrom=\"0.5\"\n                    android:valueTo=\"1\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n            </set>\n        </aapt:attr>\n    </target>\n    <target android:name=\"path\">\n        <aapt:attr name=\"android:animation\">\n            <objectAnimator\n                android:propertyName=\"fillAlpha\"\n                android:startOffset=\"25\"\n                android:duration=\"75\"\n                android:valueFrom=\"1\"\n                android:valueTo=\"0\"\n                android:valueType=\"floatType\"\n                android:interpolator=\"@android:anim/linear_interpolator\"/>\n        </aapt:attr>\n    </target>\n    <target android:name=\"path_1\">\n        <aapt:attr name=\"android:animation\">\n            <objectAnimator\n                android:propertyName=\"fillAlpha\"\n                android:startOffset=\"90\"\n                android:duration=\"30\"\n                android:valueFrom=\"0\"\n                android:valueTo=\"1\"\n                android:valueType=\"floatType\"\n                android:interpolator=\"@android:anim/linear_interpolator\"/>\n        </aapt:attr>\n    </target>\n</animated-vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/avd_send_to_mic_anim.xml",
    "content": "<animated-vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\">\n    <aapt:attr name=\"android:drawable\">\n        <vector\n            android:name=\"vector\"\n            android:width=\"24dp\"\n            android:height=\"24dp\"\n            android:viewportWidth=\"24\"\n            android:viewportHeight=\"24\">\n            <group\n                android:name=\"send\"\n                android:pivotX=\"12\"\n                android:pivotY=\"12\"\n                android:translateX=\"2\">\n                <path\n                    android:name=\"path_1\"\n                    android:pathData=\"M 3.4 20.4 L 20.85 12.92 C 21.66 12.57 21.66 11.43 20.85 11.08 L 3.4 3.6 C 2.74 3.31 2.01 3.8 2.01 4.51 L 2 9.12 C 2 9.62 2.37 10.05 2.87 10.11 L 17 12 L 2.87 13.88 C 2.37 13.95 2 14.38 2 14.88 L 2.01 19.49 C 2.01 20.2 2.74 20.69 3.4 20.4 Z\"\n                    android:fillColor=\"#000000\"\n                    android:strokeAlpha=\"0\"/>\n            </group>\n            <group\n                android:name=\"mic\"\n                android:pivotX=\"12\"\n                android:pivotY=\"12\"\n                android:scaleX=\"0.5\"\n                android:scaleY=\"0.5\">\n                <path\n                    android:name=\"path\"\n                    android:pathData=\"M 12 15 C 13.66 15 14.99 13.66 14.99 12 L 15 6 C 15 4.34 13.66 3 12 3 C 10.34 3 9 4.34 9 6 L 9 12 C 9 13.66 10.34 15 12 15 Z M 10.8 5.9 C 10.8 5.24 11.34 4.7 12 4.7 C 12.66 4.7 13.2 5.24 13.2 5.9 L 13.19 12.1 C 13.19 12.76 12.66 13.3 12 13.3 C 11.34 13.3 10.8 12.76 10.8 12.1 L 10.8 5.9 Z M 17.3 12 C 17.3 15 14.76 17.1 12 17.1 C 9.24 17.1 6.7 15 6.7 12 L 5 12 C 5 15.41 7.72 18.23 11 18.72 L 11 22 L 13 22 L 13 18.72 C 16.28 18.24 19 15.42 19 12 L 17.3 12 Z\"\n                    android:fillColor=\"#000000\"\n                    android:fillAlpha=\"0\"\n                    android:strokeAlpha=\"0\"/>\n            </group>\n        </vector>\n    </aapt:attr>\n    <target android:name=\"mic\">\n        <aapt:attr name=\"android:animation\">\n            <set>\n                <objectAnimator\n                    android:propertyName=\"scaleX\"\n                    android:startOffset=\"90\"\n                    android:duration=\"110\"\n                    android:valueFrom=\"0.5\"\n                    android:valueTo=\"1\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n                <objectAnimator\n                    android:propertyName=\"scaleY\"\n                    android:startOffset=\"90\"\n                    android:duration=\"110\"\n                    android:valueFrom=\"0.5\"\n                    android:valueTo=\"1\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n            </set>\n        </aapt:attr>\n    </target>\n    <target android:name=\"send\">\n        <aapt:attr name=\"android:animation\">\n            <set>\n                <objectAnimator\n                    android:propertyName=\"scaleX\"\n                    android:duration=\"100\"\n                    android:valueFrom=\"1\"\n                    android:valueTo=\"0.5\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n                <objectAnimator\n                    android:propertyName=\"scaleY\"\n                    android:duration=\"100\"\n                    android:valueFrom=\"1\"\n                    android:valueTo=\"0.5\"\n                    android:valueType=\"floatType\"\n                    android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"/>\n            </set>\n        </aapt:attr>\n    </target>\n    <target android:name=\"path\">\n        <aapt:attr name=\"android:animation\">\n            <objectAnimator\n                android:propertyName=\"fillAlpha\"\n                android:startOffset=\"90\"\n                android:duration=\"30\"\n                android:valueFrom=\"0\"\n                android:valueTo=\"1\"\n                android:valueType=\"floatType\"\n                android:interpolator=\"@android:anim/linear_interpolator\"/>\n        </aapt:attr>\n    </target>\n    <target android:name=\"path_1\">\n        <aapt:attr name=\"android:animation\">\n            <objectAnimator\n                android:propertyName=\"fillAlpha\"\n                android:startOffset=\"25\"\n                android:duration=\"75\"\n                android:valueFrom=\"1\"\n                android:valueTo=\"0\"\n                android:valueType=\"floatType\"\n                android:interpolator=\"@android:anim/linear_interpolator\"/>\n        </aapt:attr>\n    </target>\n</animated-vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/background_grey_ripple.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"@color/grey_500\" />"
  },
  {
    "path": "app/src/main/res/drawable/bg_dm_date_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!--<solid android:color=\"@color/deep_purple_600\" />-->\n    <solid android:color=\"?attr/dmDateHeaderBgColor\" />\n    <corners android:radius=\"16dp\" />\n    <padding\n        android:bottom=\"4dp\"\n        android:left=\"4dp\"\n        android:right=\"4dp\"\n        android:top=\"4dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_dm_time.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:angle=\"-90\"\n        android:endColor=\"@color/grey_900\"\n        android:startColor=\"#00FFFFFF\"\n        android:type=\"linear\" />\n    <corners\n        android:bottomLeftRadius=\"@dimen/dm_message_card_radius_small\"\n        android:bottomRightRadius=\"@dimen/dm_message_card_radius_small\"\n        android:topLeftRadius=\"0dp\"\n        android:topRightRadius=\"0dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/bg_indicator.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"640\"\n    android:viewportHeight=\"640\">\n    <path\n        android:fillColor=\"?colorPrimary\"\n        android:pathData=\"M640,490L490,640L640,640L640,490Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/bg_input.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/grey_600_a20\" />\n    <!--<padding-->\n    <!--    android:bottom=\"0dp\"-->\n    <!--    android:left=\"0dp\"-->\n    <!--    android:right=\"0dp\"-->\n    <!--    android:top=\"0dp\" />-->\n\n    <corners android:radius=\"24dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_media_share_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/black_a50\" />\n    <corners\n        android:bottomLeftRadius=\"@dimen/dm_message_card_radius\"\n        android:bottomRightRadius=\"@dimen/dm_message_card_radius\"\n        android:topLeftRadius=\"0dp\"\n        android:topRightRadius=\"0dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_media_share_top_incoming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/black_a50\" />\n    <corners\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\"\n        android:topLeftRadius=\"@dimen/dm_message_card_radius_small\"\n        android:topRightRadius=\"@dimen/dm_message_card_radius\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_media_share_top_outgoing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/black_a50\" />\n    <corners\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\"\n        android:topLeftRadius=\"@dimen/dm_message_card_radius\"\n        android:topRightRadius=\"@dimen/dm_message_card_radius_small\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_quote_line.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/grey_400\" />\n    <corners android:radius=\"2dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_reply_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/grey_700\" />\n    <padding\n        android:bottom=\"@dimen/dm_message_card_radius\"\n        android:left=\"@dimen/dm_message_card_radius\"\n        android:right=\"@dimen/dm_message_card_radius\"\n        android:top=\"@dimen/dm_message_card_radius\" />\n    <corners android:radius=\"@dimen/dm_message_card_radius\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_rounded_corner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"?colorSurface\" />\n    <padding\n        android:bottom=\"4dp\"\n        android:left=\"4dp\"\n        android:right=\"4dp\"\n        android:top=\"4dp\" />\n\n    <corners android:radius=\"8dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_speech_bubble_incoming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"?attr/dmIncomingBgColor\" />\n    <corners\n        android:radius=\"@dimen/dm_message_card_radius\"\n        android:topLeftRadius=\"@dimen/dm_message_card_radius_small\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_speech_bubble_outgoing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"?attr/dmOutgoingBgColor\" />\n    <corners\n        android:radius=\"@dimen/dm_message_card_radius\"\n        android:topRightRadius=\"@dimen/dm_message_card_radius_small\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/bg_user_search_input.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:bottom=\"1dp\"\n        android:left=\"-2dp\"\n        android:right=\"-2dp\"\n        android:top=\"-2dp\">\n        <shape android:shape=\"rectangle\">\n            <stroke\n                android:width=\"1dp\"\n                android:color=\"?colorControlNormal\" />\n\n            <solid android:color=\"#00FFFFFF\" />\n\n            <!--<padding android:left=\"0dp\"-->\n            <!--    android:right=\"0dp\"-->\n            <!--    android:top=\"0dp\"-->\n            <!--    android:bottom=\"0dp\" />-->\n        </shape>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_account_clock_24.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M10.63,14.1C12.23,10.58 16.38,9.03 19.9,10.63C23.42,12.23 24.97,16.38 23.37,19.9C22.24,22.4 19.75,24 17,24C14.3,24 11.83,22.44 10.67,20H1V18C1.06,16.86 1.84,15.93 3.34,15.18C4.84,14.43 6.72,14.04 9,14C9.57,14 10.11,14.05 10.63,14.1V14.1M9,4C10.12,4.03 11.06,4.42 11.81,5.17C12.56,5.92 12.93,6.86 12.93,8C12.93,9.14 12.56,10.08 11.81,10.83C11.06,11.58 10.12,11.95 9,11.95C7.88,11.95 6.94,11.58 6.19,10.83C5.44,10.08 5.07,9.14 5.07,8C5.07,6.86 5.44,5.92 6.19,5.17C6.94,4.42 7.88,4.03 9,4M17,22A5,5 0 0,0 22,17A5,5 0 0,0 17,12A5,5 0 0,0 12,17A5,5 0 0,0 17,22M16,14H17.5V16.82L19.94,18.23L19.19,19.53L16,17.69V14Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_account_multiple_remove_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M24 17V19H21V17C21 15.45 20.3 14.06 19.18 13.06C24 13.55 24 17 24 17M18 5C19.66 5 21 6.34 21 8C21 9.66 19.66 11 18 11C17.69 11 17.38 10.95 17.1 10.86C17.67 10.05 18 9.07 18 8C18 6.94 17.67 5.95 17.1 5.14C17.38 5.05 17.69 5 18 5M13 5C14.66 5 16 6.34 16 8C16 9.66 14.66 11 13 11C11.34 11 10 9.66 10 8C10 6.34 11.34 5 13 5M19 17V19H7V17C7 14.79 9.69 13 13 13C16.31 13 19 14.79 19 17M.464 13.12L2.59 11L.464 8.88L1.88 7.46L4 9.59L6.12 7.46L7.54 8.88L5.41 11L7.54 13.12L6.12 14.54L4 12.41L1.88 14.54Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M18,13h-5v5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-5H6c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h5V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v5h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_archive.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_drop_down_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M7,10l5,5 5,-5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_arrow_upward_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_baseline_check_circle_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_block_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_bookmark.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_border_style_flipped_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000000\"\n        android:pathData=\"M9,21L7,21L7,19L9,19M5,21L3,21L3,19L5,19M17,21L15,21L15,19L17,19M13,21L11,21L11,19L13,19M5,17L3,17L3,15L5,15M5,13L3,13L3,11L5,11M21,3L21,21L19,21L19,5L3,5L3,3M5,9L3,9L3,7L5,7\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_camera_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20,4h-3.17l-1.24,-1.35c-0.37,-0.41 -0.91,-0.65 -1.47,-0.65L9.88,2c-0.56,0 -1.1,0.24 -1.48,0.65L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_cancel.xml",
    "content": "<!--\n    Copyright (C) 2015 The Android Open Source Project\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n         http://www.apache.org/licenses/LICENSE-2.0\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:alpha=\"0.8\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.47,2 2,6.47 2,12c0,5.53 4.47,10 10,10c5.53,0 10,-4.47 10,-10C22,6.47 17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8s8,3.59 8,8S16.41,20 12,20z\"/>\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M15.59,7l-3.59,3.59l-3.59,-3.59l-1.41,1.41l3.59,3.59l-3.59,3.59l1.41,1.41l3.59,-3.59l3.59,3.59l1.41,-1.41l-3.59,-3.59l3.59,-3.59z\"/>\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_check_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_check_all_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"@color/ic_read_button_tint\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_multiple_blank.xml",
    "content": "<!-- drawable/checkbox_multiple_blank.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M22,16A2,2 0 0,1 20,18H8C6.89,18 6,17.1 6,16V4C6,2.89 6.89,2 8,2H20A2,2 0 0,1 22,4V16M16,20V22H4A2,2 0 0,1 2,20V7H4V20H16Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_checkbox_multiple_blank_stroke.xml",
    "content": "<!-- drawable/checkbox_multiple_blank.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@color/white\"\n        android:pathData=\"M22,16A2,2 0 0,1 20,18H8C6.89,18 6,17.1 6,16V4C6,2.89 6.89,2 8,2H20A2,2 0 0,1 22,4V16M16,20V22H4A2,2 0 0,1 2,20V7H4V20H16Z\"\n        android:strokeWidth=\"0.4\"\n        android:strokeColor=\"@color/black_a50\"\n        android:strokeLineJoin=\"round\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_circle_check.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_round_check_circle_24\" android:state_selected=\"true\" />\n    <item android:drawable=\"@drawable/ic_radio_button_unchecked_24\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_class_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_clock_alert_outline_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M11 7V13L16.2 16.1L17 14.9L12.5 12.2V7H11M20 12V18H22V12H20M20 20V22H22V20H20M18 20C16.3 21.3 14.3 22 12 22C6.5 22 2 17.5 2 12S6.5 2 12 2C16.8 2 20.9 5.4 21.8 10H19.7C18.8 6.6 15.7 4 12 4C7.6 4 4 7.6 4 12S7.6 20 12 20C14.4 20 16.5 18.9 18 17.3V20Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_close_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_cloud_download_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_dashboard_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_delete.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_download.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,12v7L5,19v-7L3,12v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2zM13,12.67l2.59,-2.58L17,11.5l-5,5 -5,-5 1.41,-1.41L11,12.67L11,3h2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_download_circle_24.xml",
    "content": "<!-- drawable/download_circle.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M12 2C17.5 2 22 6.5 22 12C22 17.5 17.5 22 12 22C6.5 22 2 17.5 2 12C2 6.5 6.5 2 12 2M8 17H16V15H8V17M16 10H13.5V6H10.5V10H8L12 14L16 10Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_explore_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_face_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M8.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,16c-1.48,0 -2.75,-0.81 -3.45,-2L6.88,14c0.8,2.05 2.79,3.5 5.12,3.5s4.32,-1.45 5.12,-3.5h-1.67c-0.69,1.19 -1.97,2 -3.45,2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_file_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_folder_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_forward_5_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8c4.42,0 8,-3.58 8,-8H18z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12.03,15.38c-0.44,0 -0.58,-0.31 -0.6,-0.56h-0.84c0.03,0.85 0.79,1.25 1.44,1.25c0.93,0 1.44,-0.63 1.44,-1.43c0,-1.33 -0.97,-1.44 -1.3,-1.44c-0.2,0 -0.43,0.05 -0.64,0.16l0.11,-0.92h1.7v-0.71h-2.39l-0.25,2.17l0.67,0.17c0.13,-0.13 0.28,-0.23 0.57,-0.23c0.4,0 0.69,0.23 0.69,0.75C12.62,14.64 12.65,15.38 12.03,15.38z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_forward_5_24_a50.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8c4.42,0 8,-3.58 8,-8H18z\" />\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12.03,15.38c-0.44,0 -0.58,-0.31 -0.6,-0.56h-0.84c0.03,0.85 0.79,1.25 1.44,1.25c0.93,0 1.44,-0.63 1.44,-1.43c0,-1.33 -0.97,-1.44 -1.3,-1.44c-0.2,0 -0.43,0.05 -0.64,0.16l0.11,-0.92h1.7v-0.71h-2.39l-0.25,2.17l0.67,0.17c0.13,-0.13 0.28,-0.23 0.57,-0.23c0.4,0 0.69,0.23 0.69,0.75C12.62,14.64 12.65,15.38 12.03,15.38z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_forward_5_24_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_forward_5_24_a50\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/ic_forward_5_24\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_highlight_off_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_home_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_image_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,5v14L5,19L5,5h14m0,-2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM14.14,11.86l-3,3.87L9,13.14 6,17h12l-3.86,-5.14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keyboard_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keyboard_arrow_down_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_keyboard_arrow_up_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    android:height=\"108dp\"\n    android:width=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#3DDC84\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_like.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_logout_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_message_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_more_horiz_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_more_vert_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_not_liked.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_notes_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,18h12v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h18v-2L3,11v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_notif.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"#333333\"\n    android:alpha=\"0.6\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_open_in_new_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_class_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M18,2L6,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM9,4h2v5l-1,-0.75L9,9L9,4zM18,20L6,20L6,4h1v9l3,-2.25L13,13L13,4h5v16z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_comments_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9M10,16V19.08L13.08,16H20V4H4V16H10Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_info_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_map_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48L3,20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48L21,3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM10,5.47l4,1.4v11.66l-4,-1.4L10,5.47zM5,6.46l3,-1.01v11.7l-3,1.16L5,6.46zM19,17.54l-3,1.01L16,6.86l3,-1.16v11.84z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_person_add_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM15,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4zM9,18c0.22,-0.72 3.31,-2 6,-2 2.7,0 5.8,1.29 6,2L9,18zM6,15v-3h3v-2L6,10L6,7L4,7v3L1,10v2h3v3z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_person_add_disabled_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15,6c1.1,0 2,0.9 2,2 0,0.99 -0.73,1.82 -1.67,1.97l-2.31,-2.31C13.19,6.72 14.01,6 15,6m0,-2c-2.21,0 -4,1.79 -4,4 0,0.18 0.03,0.35 0.05,0.52l3.43,3.43c0.17,0.02 0.34,0.05 0.52,0.05 2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4zM16.69,14.16L22.53,20L23,20v-2c0,-2.14 -3.56,-3.5 -6.31,-3.84zM13.01,16.13L14.88,18L9,18c0.08,-0.24 0.88,-1.01 2.91,-1.57l1.1,-0.3M1.41,1.71L0,3.12l4,4L4,10L1,10v2h3v3h2v-3h2.88l2.51,2.51C9.19,15.11 7,16.3 7,18v2h9.88l4,4 1.41,-1.41L1.41,1.71zM6,10v-0.88l0.88,0.88L6,10z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_person_pin_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,2L5,2c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM19,18h-4.83l-0.59,0.59L12,20.17l-1.59,-1.59 -0.58,-0.58L5,18L5,4h14v14zM12,11c1.65,0 3,-1.35 3,-3s-1.35,-3 -3,-3 -3,1.35 -3,3 1.35,3 3,3zM12,7c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM18,15.58c0,-2.5 -3.97,-3.58 -6,-3.58s-6,1.08 -6,3.58L6,17h12v-1.42zM8.48,15c0.74,-0.51 2.23,-1 3.52,-1s2.78,0.49 3.52,1L8.48,15z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_settings_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_star_plus_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M5.8 21L7.4 14L2 9.2L9.2 8.6L12 2L14.8 8.6L22 9.2L18.8 12H18C17.3 12 16.6 12.1 15.9 12.4L18.1 10.5L13.7 10.1L12 6.1L10.3 10.1L5.9 10.5L9.2 13.4L8.2 17.7L12 15.4L12.5 15.7C12.3 16.2 12.1 16.8 12.1 17.3L5.8 21M17 14V17H14V19H17V22H19V19H22V17H19V14H17Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_outline_views_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,6.5c3.79,0 7.17,2.13 8.82,5.5 -1.65,3.37 -5.02,5.5 -8.82,5.5S4.83,15.37 3.18,12C4.83,8.63 8.21,6.5 12,6.5m0,-2C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5 -2.5,-1.12 -2.5,-2.5 1.12,-2.5 2.5,-2.5m0,-2c-2.48,0 -4.5,2.02 -4.5,4.5s2.02,4.5 4.5,4.5 4.5,-2.02 4.5,-4.5 -2.02,-4.5 -4.5,-4.5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_pause_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_person_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_photo_filter.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000000\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M9.5,2C12.8843,2 15.7451,4.2416 16.6786,7.3211C19.7584,8.2549 22,11.1157 22,14.5C22,18.6421 18.6421,22 14.5,22C11.1157,22 8.2549,19.7584 7.3214,16.6789C4.2416,15.7451 2,12.8843 2,9.5C2,5.3579 5.3579,2 9.5,2ZM16.9984,9.5989L17,9.5C17,13.6089 13.6958,16.9461 9.5995,16.9994C10.5089,18.7801 12.362,20 14.5,20C17.5376,20 20,17.5376 20,14.5C20,12.362 18.7801,10.5089 16.9984,9.5989ZM9.5,4C6.4624,4 4,6.4624 4,9.5C4,11.638 5.2199,13.4911 7.0016,14.4011L7,14.5C7,10.3913 10.3039,7.0542 14.3999,7.0007C13.4911,5.2199 11.638,4 9.5,4Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_play_arrow_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M8,5v14l11,-7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_play_arrow_24_a50.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:fillAlpha=\"0.5\"\n      android:pathData=\"M8,5v14l11,-7z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_play_circle_outline_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_play_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_play_arrow_24_a50\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/ic_play_arrow_24\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_profile_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_profile_40.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"40dp\"\n    android:height=\"40dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_profile_48.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"48dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_radio_button_unchecked_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_refresh_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_replay_5_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.42,5 12,5z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10.69,13.9l0.25,-2.17h2.39v0.71h-1.7l-0.11,0.92c0.03,-0.02 0.07,-0.03 0.11,-0.05s0.09,-0.04 0.15,-0.05s0.12,-0.03 0.18,-0.04s0.13,-0.02 0.2,-0.02c0.21,0 0.39,0.03 0.55,0.1s0.3,0.16 0.41,0.28s0.2,0.27 0.25,0.45s0.09,0.38 0.09,0.6c0,0.19 -0.03,0.37 -0.09,0.54s-0.15,0.32 -0.27,0.45s-0.27,0.24 -0.45,0.31s-0.39,0.12 -0.64,0.12c-0.18,0 -0.36,-0.03 -0.53,-0.08s-0.32,-0.14 -0.46,-0.24s-0.24,-0.24 -0.32,-0.39s-0.13,-0.33 -0.13,-0.53h0.84c0.02,0.18 0.08,0.32 0.19,0.41s0.25,0.15 0.42,0.15c0.11,0 0.2,-0.02 0.27,-0.06s0.14,-0.1 0.18,-0.17s0.08,-0.15 0.11,-0.25s0.03,-0.2 0.03,-0.31s-0.01,-0.21 -0.04,-0.31s-0.07,-0.17 -0.13,-0.24s-0.13,-0.12 -0.21,-0.15s-0.19,-0.05 -0.3,-0.05c-0.08,0 -0.15,0.01 -0.2,0.02s-0.11,0.03 -0.15,0.05s-0.08,0.05 -0.12,0.07s-0.07,0.06 -0.1,0.09L10.69,13.9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_replay_5_24_a50.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.42,5 12,5z\" />\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M10.69,13.9l0.25,-2.17h2.39v0.71h-1.7l-0.11,0.92c0.03,-0.02 0.07,-0.03 0.11,-0.05s0.09,-0.04 0.15,-0.05s0.12,-0.03 0.18,-0.04s0.13,-0.02 0.2,-0.02c0.21,0 0.39,0.03 0.55,0.1s0.3,0.16 0.41,0.28s0.2,0.27 0.25,0.45s0.09,0.38 0.09,0.6c0,0.19 -0.03,0.37 -0.09,0.54s-0.15,0.32 -0.27,0.45s-0.27,0.24 -0.45,0.31s-0.39,0.12 -0.64,0.12c-0.18,0 -0.36,-0.03 -0.53,-0.08s-0.32,-0.14 -0.46,-0.24s-0.24,-0.24 -0.32,-0.39s-0.13,-0.33 -0.13,-0.53h0.84c0.02,0.18 0.08,0.32 0.19,0.41s0.25,0.15 0.42,0.15c0.11,0 0.2,-0.02 0.27,-0.06s0.14,-0.1 0.18,-0.17s0.08,-0.15 0.11,-0.25s0.03,-0.2 0.03,-0.31s-0.01,-0.21 -0.04,-0.31s-0.07,-0.17 -0.13,-0.24s-0.13,-0.12 -0.21,-0.15s-0.19,-0.05 -0.3,-0.05c-0.08,0 -0.15,0.01 -0.2,0.02s-0.11,0.03 -0.15,0.05s-0.08,0.05 -0.12,0.07s-0.07,0.06 -0.1,0.09L10.69,13.9z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_replay_5_24_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_replay_5_24_a50\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/ic_replay_5_24\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_add_circle_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM16,13h-3v3c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-3L8,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h3L11,8c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v3h3c0.55,0 1,0.45 1,1s-0.45,1 -1,1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_arrow_back_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,11H7.83l4.88,-4.88c0.39,-0.39 0.39,-1.03 0,-1.42 -0.39,-0.39 -1.02,-0.39 -1.41,0l-6.59,6.59c-0.39,0.39 -0.39,1.02 0,1.41l6.59,6.59c0.39,0.39 1.02,0.39 1.41,0 0.39,-0.39 0.39,-1.02 0,-1.41L7.83,13H19c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_attach_file_rot45_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <group\n        android:rotation=\"45\"\n        android:translateX=\"12\"\n        android:translateY=\"-3\">\n        <path\n            android:fillColor=\"@android:color/white\"\n            android:pathData=\"M16.5,6.75v10.58c0,2.09 -1.53,3.95 -3.61,4.15 -2.39,0.23 -4.39,-1.64 -4.39,-3.98V5.14c0,-1.31 0.94,-2.5 2.24,-2.63 1.5,-0.15 2.76,1.02 2.76,2.49v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6.75c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v8.61c0,1.31 0.94,2.5 2.24,2.63 1.5,0.15 2.76,-1.02 2.76,-2.49V5.17c0,-2.09 -1.53,-3.95 -3.61,-4.15C9.01,0.79 7,2.66 7,5v12.27c0,2.87 2.1,5.44 4.96,5.71 3.29,0.3 6.04,-2.26 6.04,-5.48V6.75c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_backspace_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0.37,11.45c-0.22,0.34 -0.22,0.77 0,1.11l5.04,7.56c0.36,0.52 0.9,0.88 1.59,0.88h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM18.3,16.3c-0.39,0.39 -1.02,0.39 -1.41,0L14,13.41l-2.89,2.89c-0.39,0.39 -1.02,0.39 -1.41,0 -0.39,-0.39 -0.39,-1.02 0,-1.41L12.59,12 9.7,9.11c-0.39,-0.39 -0.39,-1.02 0,-1.41 0.39,-0.39 1.02,-0.39 1.41,0L14,10.59l2.89,-2.89c0.39,-0.39 1.02,-0.39 1.41,0 0.39,0.39 0.39,1.02 0,1.41L15.41,12l2.89,2.89c0.38,0.38 0.38,1.02 0,1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_bookmark_border_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M17,3L7,3c-1.1,0 -2,0.9 -2,2v16l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,6c0,-0.55 0.45,-1 1,-1h8c0.55,0 1,0.45 1,1v12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_check_circle_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM9.29,16.29L5.7,12.7c-0.39,-0.39 -0.39,-1.02 0,-1.41 0.39,-0.39 1.02,-0.39 1.41,0L10,14.17l6.88,-6.88c0.39,-0.39 1.02,-0.39 1.41,0 0.39,0.39 0.39,1.02 0,1.41l-7.59,7.59c-0.38,0.39 -1.02,0.39 -1.41,0z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_crop_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M17,15h2L19,7c0,-1.1 -0.9,-2 -2,-2L9,5v2h7c0.55,0 1,0.45 1,1v7zM8,17c-0.55,0 -1,-0.45 -1,-1L7,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v3L2,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h3v10c0,1.1 0.9,2 2,2h10v3c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-3h3c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L8,17z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_drag_handle_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,9H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h14c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1zM5,15h14c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_edit_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,17.46v3.04c0,0.28 0.22,0.5 0.5,0.5h3.04c0.13,0 0.26,-0.05 0.35,-0.15L17.81,9.94l-3.75,-3.75L3.15,17.1c-0.1,0.1 -0.15,0.22 -0.15,0.36zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_emotions_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M11.99,2C6.47,2 2,6.48 2,12c0,5.52 4.47,10 9.99,10C17.52,22 22,17.52 22,12C22,6.48 17.52,2 11.99,2zM8.5,8C9.33,8 10,8.67 10,9.5S9.33,11 8.5,11S7,10.33 7,9.5S7.67,8 8.5,8zM16.71,14.72C15.8,16.67 14.04,18 12,18s-3.8,-1.33 -4.71,-3.28C7.13,14.39 7.37,14 7.74,14h8.52C16.63,14 16.87,14.39 16.71,14.72zM15.5,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S14.67,8 15.5,8S17,8.67 17,9.5S16.33,11 15.5,11z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_events_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,5h-2V4c0,-0.55 -0.45,-1 -1,-1H8C7.45,3 7,3.45 7,4v1H5C3.9,5 3,5.9 3,7v1c0,2.55 1.92,4.63 4.39,4.94c0.63,1.5 1.98,2.63 3.61,2.96V19H8c-0.55,0 -1,0.45 -1,1v0c0,0.55 0.45,1 1,1h8c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1h-3v-3.1c1.63,-0.33 2.98,-1.46 3.61,-2.96C19.08,12.63 21,10.55 21,8V7C21,5.9 20.1,5 19,5zM5,8V7h2v3.82C5.84,10.4 5,9.3 5,8zM19,8c0,1.3 -0.84,2.4 -2,2.82V7h2V8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_flags_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,9h-5l-0.72,-1.45C13.11,7.21 12.76,7 12.38,7H7V5.72C7.6,5.38 8,4.74 8,4c0,-1.1 -0.9,-2 -2,-2S4,2.9 4,4c0,0.74 0.4,1.38 1,1.72V20c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-3h5l0.72,1.45c0.17,0.34 0.52,0.55 0.89,0.55H19c0.55,0 1,-0.45 1,-1v-8C20,9.45 19.55,9 19,9zM18,17h-4l-1,-2H7V9h5l1,2h5V17z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_food_beverage_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,19H3c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h16c0.55,0 1,-0.45 1,-1S19.55,19 19,19z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20,3H9v2.4l1.81,1.45C10.93,6.94 11,7.09 11,7.24v4.26c0,0.28 -0.22,0.5 -0.5,0.5h-4C6.22,12 6,11.78 6,11.5V7.24c0,-0.15 0.07,-0.3 0.19,-0.39L8,5.4V3H6C4.9,3 4,3.9 4,5v8c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.1,0 2,-0.9 2,-2V5C22,3.9 21.1,3 20,3zM20,8h-2V5h2V8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_nature_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M21.94,4.88C21.76,4.35 21.25,4 20.68,4c-0.03,0 -0.06,0 -0.09,0H19.6l-0.31,-0.97C19.15,2.43 18.61,2 18,2h0c-0.61,0 -1.15,0.43 -1.29,1.04L16.4,4h-0.98c-0.03,0 -0.06,0 -0.09,0c-0.57,0 -1.08,0.35 -1.26,0.88c-0.19,0.56 0.04,1.17 0.56,1.48l0.87,0.52L15.1,8.12c-0.23,0.58 -0.04,1.25 0.45,1.62C15.78,9.91 16.06,10 16.33,10c0.31,0 0.61,-0.11 0.86,-0.32L18,8.98l0.81,0.7C19.06,9.89 19.36,10 19.67,10c0.27,0 0.55,-0.09 0.78,-0.26c0.5,-0.37 0.68,-1.04 0.45,-1.62l-0.39,-1.24l0.87,-0.52C21.89,6.05 22.12,5.44 21.94,4.88zM18,7c-0.55,0 -1,-0.45 -1,-1c0,-0.55 0.45,-1 1,-1s1,0.45 1,1C19,6.55 18.55,7 18,7z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15.5,12h-1.09c-0.19,-0.54 -0.49,-1.05 -0.93,-1.49S12.54,9.78 12,9.59V8.5C12,8.22 11.78,8 11.5,8S11,8.22 11,8.5v0.88c-0.11,-0.01 -0.23,-0.03 -0.34,-0.03c-1.02,0 -2.05,0.39 -2.83,1.17c-0.16,0.16 -0.3,0.34 -0.43,0.53L6,10.52c-1.56,-0.55 -3.28,0.27 -3.83,1.82c0,0 0,0 0,0c-0.27,0.75 -0.23,1.57 0.12,2.29c0.23,0.48 0.58,0.87 1,1.16c-0.38,1.35 -0.06,2.85 1,3.91c1.06,1.06 2.57,1.38 3.91,1c0.29,0.42 0.68,0.77 1.16,1C9.78,21.9 10.21,22 10.65,22c0.34,0 0.68,-0.06 1.01,-0.17c0,0 0,0 0,0c1.56,-0.55 2.38,-2.27 1.82,-3.85l-0.52,-1.37c0.18,-0.13 0.36,-0.27 0.53,-0.43c0.87,-0.87 1.24,-2.04 1.14,-3.17h0.88c0.28,0 0.5,-0.22 0.5,-0.5C16,12.22 15.78,12 15.5,12zM4.67,14.29c-0.25,-0.09 -0.45,-0.27 -0.57,-0.51s-0.13,-0.51 -0.04,-0.76c0.19,-0.52 0.76,-0.79 1.26,-0.61l3.16,1.19C7.33,14.2 5.85,14.71 4.67,14.29zM10.99,19.94c-0.25,0.09 -0.52,0.08 -0.76,-0.04c-0.24,-0.11 -0.42,-0.32 -0.51,-0.57c-0.42,-1.18 0.09,-2.65 0.7,-3.8l1.18,3.13C11.78,19.18 11.51,19.76 10.99,19.94zM12.2,14.6l-0.61,-1.61c0,-0.01 -0.01,-0.02 -0.02,-0.03c-0.02,-0.04 -0.04,-0.08 -0.06,-0.12c-0.02,-0.04 -0.04,-0.07 -0.07,-0.11c-0.03,-0.03 -0.06,-0.06 -0.09,-0.09c-0.03,-0.03 -0.06,-0.06 -0.09,-0.09c-0.03,-0.03 -0.07,-0.05 -0.11,-0.07c-0.04,-0.02 -0.07,-0.05 -0.12,-0.06c-0.01,0 -0.02,-0.01 -0.03,-0.02L9.4,11.8c0.36,-0.29 0.79,-0.46 1.26,-0.46c0.53,0 1.04,0.21 1.41,0.59C12.8,12.66 12.84,13.81 12.2,14.6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_objects_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,3c-0.46,0 -0.93,0.04 -1.4,0.14C7.84,3.67 5.64,5.9 5.12,8.66c-0.48,2.61 0.48,5.01 2.22,6.56C7.77,15.6 8,16.13 8,16.69V19c0,1.1 0.9,2 2,2h0.28c0.35,0.6 0.98,1 1.72,1s1.38,-0.4 1.72,-1H14c1.1,0 2,-0.9 2,-2v-2.31c0,-0.55 0.22,-1.09 0.64,-1.46C18.09,13.95 19,12.08 19,10C19,6.13 15.87,3 12,3zM12.5,14h-1v-2.59L9.67,9.59l0.71,-0.71L12,10.5l1.62,-1.62l0.71,0.71l-1.83,1.83V14zM13.5,19c-0.01,0 -0.02,-0.01 -0.03,-0.01V19h-2.94v-0.01c-0.01,0 -0.02,0.01 -0.03,0.01c-0.28,0 -0.5,-0.22 -0.5,-0.5c0,-0.28 0.22,-0.5 0.5,-0.5c0.01,0 0.02,0.01 0.03,0.01V18h2.94v0.01c0.01,0 0.02,-0.01 0.03,-0.01c0.28,0 0.5,0.22 0.5,0.5C14,18.78 13.78,19 13.5,19zM13.5,17h-3c-0.28,0 -0.5,-0.22 -0.5,-0.5c0,-0.28 0.22,-0.5 0.5,-0.5h3c0.28,0 0.5,0.22 0.5,0.5C14,16.78 13.78,17 13.5,17z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_symbols_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,5H4C3.45,5 3,5.45 3,6c0,0.55 0.45,1 1,1h2v3c0,0.55 0.45,1 1,1s1,-0.45 1,-1V7h2c0.55,0 1,-0.45 1,-1C11,5.45 10.55,5 10,5z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,2H4C3.45,2 3,2.45 3,3s0.45,1 1,1h6c0.55,0 1,-0.45 1,-1S10.55,2 10,2z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20.89,13.11c-0.39,-0.39 -1.02,-0.39 -1.41,0l-6.36,6.36c-0.39,0.39 -0.39,1.02 0,1.41c0.39,0.39 1.02,0.39 1.41,0l6.36,-6.36C21.28,14.13 21.28,13.5 20.89,13.11z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M14.5,14.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19.5,19.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15.5,11c1.38,0 2.5,-1.12 2.5,-2.5V4h2c0.55,0 1,-0.45 1,-1c0,-0.55 -0.45,-1 -1,-1h-2c-0.55,0 -1,0.45 -1,1v3.51C16.58,6.19 16.07,6 15.5,6C14.12,6 13,7.12 13,8.5C13,9.88 14.12,11 15.5,11z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10.45,18.09c0.39,-0.39 0.39,-1.02 0,-1.41c-0.39,-0.39 -1.02,-0.39 -1.41,0l-0.71,0.71l-0.71,-0.71l0.35,-0.35c0.98,-0.98 0.98,-2.56 0,-3.54h0c-0.49,-0.49 -1.13,-0.73 -1.77,-0.73c-0.64,0 -1.28,0.24 -1.77,0.73c-0.98,0.98 -0.98,2.56 0,3.54l0.35,0.35l-1.06,1.06c-0.98,0.98 -0.98,2.56 0,3.54C4.22,21.76 4.86,22 5.5,22c0.64,0 1.28,-0.24 1.77,-0.73l1.06,-1.06l0.71,0.71c0.39,0.39 1.02,0.39 1.41,0c0.39,-0.39 0.39,-1.02 0,-1.41l-0.71,-0.71v0L10.45,18.09zM5.85,14.2c0.12,-0.12 0.26,-0.15 0.35,-0.15s0.23,0.03 0.35,0.15c0.19,0.2 0.19,0.51 0,0.71l-0.35,0.35L5.85,14.9c-0.12,-0.12 -0.15,-0.26 -0.15,-0.35S5.73,14.32 5.85,14.2zM5.85,19.85C5.73,19.97 5.59,20 5.5,20s-0.23,-0.03 -0.35,-0.15C5.03,19.73 5,19.59 5,19.5s0.03,-0.23 0.15,-0.35l1.06,-1.06l0.71,0.71L5.85,19.85z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_emoji_transportation_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M21.99,14.77l-1.43,-4.11c-0.14,-0.4 -0.52,-0.66 -0.97,-0.66H12.4c-0.46,0 -0.83,0.26 -0.98,0.66L10,14.77v5.24c0,0.55 0.45,0.99 1,0.99s1,-0.45 1,-1v-1h8v1c0,0.55 0.45,1 1,1s0.99,-0.44 1,-0.99L21.99,14.77zM11.61,13.34l0.69,-2c0.05,-0.2 0.24,-0.34 0.46,-0.34h6.48c0.21,0 0.4,0.14 0.47,0.34l0.69,2c0.11,0.32 -0.13,0.66 -0.47,0.66h-7.85C11.74,14 11.5,13.66 11.61,13.34zM11.99,17c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1s1,0.45 1,1S12.54,17 11.99,17zM19.99,17c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1s1,0.45 1,1S20.54,17 19.99,17z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M14,4.5V9h1V4c0,-0.55 -0.45,-1 -1,-1H8C7.45,3 7,3.45 7,4v4H3C2.45,8 2,8.45 2,9v12h1V9.5C3,9.22 3.22,9 3.5,9h4C7.78,9 8,8.78 8,8.5v-4C8,4.22 8.22,4 8.5,4h5C13.78,4 14,4.22 14,4.5z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M5,11h2v2h-2z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,5h2v2h-2z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M5,15h2v2h-2z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M5,19h2v2h-2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_flip_camera_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M9,12c0,1.66 1.34,3 3,3s3,-1.34 3,-3s-1.34,-3 -3,-3S9,10.34 9,12z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M8,9L8,9c0,-0.55 -0.45,-1 -1,-1H5.09C6.47,5.61 9.05,4 12,4c3.49,0 6.45,2.24 7.54,5.36C19.68,9.75 20.07,10 20.48,10h0c0.68,0 1.18,-0.67 0.96,-1.31C20.07,4.79 16.36,2 12,2C8.73,2 5.82,3.58 4,6.01V5c0,-0.55 -0.45,-1 -1,-1h0C2.45,4 2,4.45 2,5v4c0,0.55 0.45,1 1,1h4C7.55,10 8,9.55 8,9z\"/>\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M16,15L16,15c0,0.55 0.45,1 1,1h1.91c-1.38,2.39 -3.96,4 -6.91,4c-3.49,0 -6.45,-2.24 -7.54,-5.36C4.32,14.25 3.93,14 3.52,14h0c-0.68,0 -1.18,0.67 -0.96,1.31C3.93,19.21 7.64,22 12,22c3.27,0 6.18,-1.58 8,-4.01V19c0,0.55 0.45,1 1,1h0c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1h-4C16.45,14 16,14.45 16,15z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_gif_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12.25,9c0.41,0 0.75,0.34 0.75,0.75v4.5c0,0.41 -0.34,0.75 -0.75,0.75s-0.75,-0.34 -0.75,-0.75v-4.5c0,-0.41 0.34,-0.75 0.75,-0.75zM10,9.75c0,-0.41 -0.34,-0.75 -0.75,-0.75L6,9c-0.6,0 -1,0.5 -1,1v4c0,0.5 0.4,1 1,1h3c0.6,0 1,-0.5 1,-1v-1.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v0.75h-2v-3h2.75c0.41,0 0.75,-0.34 0.75,-0.75zM19,9.75c0,-0.41 -0.34,-0.75 -0.75,-0.75L15.5,9c-0.55,0 -1,0.45 -1,1v4.25c0,0.41 0.34,0.75 0.75,0.75s0.75,-0.34 0.75,-0.75L16,13h1.25c0.41,0 0.75,-0.34 0.75,-0.75s-0.34,-0.75 -0.75,-0.75L16,11.5v-1h2.25c0.41,0 0.75,-0.34 0.75,-0.75z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_location_on_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C8.13,2 5,5.13 5,9c0,4.17 4.42,9.92 6.24,12.11 0.4,0.48 1.13,0.48 1.53,0C14.58,18.92 19,13.17 19,9c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_mode_comment_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9Z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_pause_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M8,19c1.1,0 2,-0.9 2,-2L10,7c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2v10c0,1.1 0.9,2 2,2zM14,7v10c0,1.1 0.9,2 2,2s2,-0.9 2,-2L18,7c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_play_arrow_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M8,6.82v10.36c0,0.79 0.87,1.27 1.54,0.84l8.14,-5.18c0.62,-0.39 0.62,-1.29 0,-1.69L9.54,5.98C8.87,5.55 8,6.03 8,6.82z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_remove_circle_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM16,13L8,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_reply_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M10,9V7.41c0,-0.89 -1.08,-1.34 -1.71,-0.71L3.7,11.29c-0.39,0.39 -0.39,1.02 0,1.41l4.59,4.59c0.63,0.63 1.71,0.19 1.71,-0.7V14.9c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_send_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:autoMirrored=\"true\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3.4,20.4l17.45,-7.48c0.81,-0.35 0.81,-1.49 0,-1.84L3.4,3.6c-0.66,-0.29 -1.39,0.2 -1.39,0.91L2,9.12c0,0.5 0.37,0.93 0.87,0.99L17,12 2.87,13.88c-0.5,0.07 -0.87,0.5 -0.87,1l0.01,4.61c0,0.71 0.73,1.2 1.39,0.91z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_tune_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,18c0,0.55 0.45,1 1,1h5v-2L4,17c-0.55,0 -1,0.45 -1,1zM3,6c0,0.55 0.45,1 1,1h9L13,5L4,5c-0.55,0 -1,0.45 -1,1zM13,20v-1h7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-7v-1c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4c0,0.55 0.45,1 1,1s1,-0.45 1,-1zM7,10v1L4,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h3v1c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1zM21,12c0,-0.55 -0.45,-1 -1,-1h-9v2h9c0.55,0 1,-0.45 1,-1zM16,9c0.55,0 1,-0.45 1,-1L17,7h3c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-3L17,4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4c0,0.55 0.45,1 1,1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_round_unknown_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92c-0.5,0.51 -0.86,0.97 -1.04,1.69 -0.08,0.32 -0.13,0.68 -0.13,1.14h-2v-0.5c0,-0.46 0.08,-0.9 0.22,-1.31 0.2,-0.58 0.53,-1.1 0.95,-1.52l1.24,-1.26c0.46,-0.44 0.68,-1.1 0.55,-1.8 -0.13,-0.72 -0.69,-1.33 -1.39,-1.53 -1.11,-0.31 -2.14,0.32 -2.47,1.27 -0.12,0.37 -0.43,0.65 -0.82,0.65h-0.3C8.4,9 8,8.44 8.16,7.88c0.43,-1.47 1.68,-2.59 3.23,-2.83 1.52,-0.24 2.97,0.55 3.87,1.8 1.18,1.63 0.83,3.38 -0.19,4.4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_rounded_corner_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M19,19h2v2h-2V19zM19,17h2v-2h-2V17zM3,13h2v-2H3V13zM3,17h2v-2H3V17zM3,9h2V7H3V9zM3,5h2V3H3V5zM7,5h2V3H7V5zM15,21h2v-2h-2V21zM11,21h2v-2h-2V21zM15,21h2v-2h-2V21zM7,21h2v-2H7V21zM3,21h2v-2H3V21zM21,8c0,-2.76 -2.24,-5 -5,-5h-5v2h5c1.65,0 3,1.35 3,3v5h2V8z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_search_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_settings_backup_restore_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_shutter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_shutter_pressed\" />\n    <item android:state_focused=\"true\" android:drawable=\"@drawable/ic_shutter_focused\" />\n    <item android:drawable=\"@drawable/ic_shutter_normal\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_shutter_focused.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path\n        android:fillColor=\"@color/white_a50\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"@android:color/transparent\" />\n    <path\n        android:fillColor=\"@color/white\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeWidth=\"2\"\n        android:strokeColor=\"#4F9BF8\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_shutter_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path\n        android:fillColor=\"@color/white_a50\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"@android:color/transparent\" />\n    <path\n        android:fillColor=\"@color/white\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"@android:color/transparent\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_shutter_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path\n        android:fillColor=\"@color/white_a50\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"@android:color/transparent\" />\n    <path\n        android:fillColor=\"#C3C2C2\"\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"@android:color/transparent\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_slider_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_star_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_star_check_24.xml",
    "content": "<!-- drawable/star_check.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"24dp\"\n    android:width=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path android:fillColor=\"#000\" android:pathData=\"M5.8 21L7.4 14L2 9.2L9.2 8.6L12 2L14.8 8.6L22 9.2L18.8 12H18C14.9 12 12.4 14.3 12 17.3L5.8 21M17.8 21.2L22.6 16.4L21.3 15L17.7 18.6L16.2 17L15 18.2L17.8 21.2\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_sticker_curved_outlines.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:pathData=\"M22.684,10.469C22.755,10.97 22.792,11.481 22.792,12C22.792,17.956 17.956,22.792 12,22.792C6.044,22.792 1.208,17.956 1.208,12C1.208,6.044 6.044,1.208 12,1.208C12.302,1.208 12.601,1.22 12.897,1.245C14.309,2.434 21.296,9.14 22.684,10.469Z\"\n      android:strokeWidth=\"1.73\"\n      android:strokeColor=\"@android:color/white\"/>\n  <path\n      android:pathData=\"M22.684,10.469C19.595,11.407 16.333,11.7 14,9.469C11.769,7.336 11.975,4.191 12.897,1.245\"\n      android:strokeWidth=\"1.73\"\n      android:fillColor=\"@android:color/white\"\n      android:strokeColor=\"@android:color/white\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_story_list.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM11,4.07L11,2.05c-2.01,0.2 -3.84,1 -5.32,2.21L7.1,5.69c1.11,-0.86 2.44,-1.44 3.9,-1.62zM5.69,7.1L4.26,5.68C3.05,7.16 2.25,8.99 2.05,11h2.02c0.18,-1.46 0.76,-2.79 1.62,-3.9zM4.07,13L2.05,13c0.2,2.01 1,3.84 2.21,5.32l1.43,-1.43c-0.86,-1.1 -1.44,-2.43 -1.62,-3.89zM5.68,19.74C7.16,20.95 9,21.75 11,21.95v-2.02c-1.46,-0.18 -2.79,-0.76 -3.9,-1.62l-1.42,1.43zM22,12c0,5.16 -3.92,9.42 -8.95,9.95v-2.02C16.97,19.41 20,16.05 20,12s-3.03,-7.41 -6.95,-7.93L13.05,2.05C18.08,2.58 22,6.84 22,12z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_story_viewer_list.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M7,19h10L17,4L7,4v15zM2,17h4L6,6L2,6v11zM18,6v11h4L22,6h-4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_submit.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_suggested_users.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M11,14H9c0,-4.97 4.03,-9 9,-9v2C14.13,7 11,10.13 11,14zM18,11V9c-2.76,0 -5,2.24 -5,5h2C15,12.34 16.34,11 18,11zM7,4c0,-1.11 -0.89,-2 -2,-2S3,2.89 3,4s0.89,2 2,2S7,5.11 7,4zM11.45,4.5h-2C9.21,5.92 7.99,7 6.5,7h-3C2.67,7 2,7.67 2,8.5V11h6V8.74C9.86,8.15 11.25,6.51 11.45,4.5zM19,17c1.11,0 2,-0.89 2,-2s-0.89,-2 -2,-2s-2,0.89 -2,2S17.89,17 19,17zM20.5,18h-3c-1.49,0 -2.71,-1.08 -2.95,-2.5h-2c0.2,2.01 1.59,3.65 3.45,4.24V22h6v-2.5C22,18.67 21.33,18 20.5,18z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_unread_indicator_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"@color/blue_400\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_video_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:strokeColor=\"@color/black\"\n        android:strokeWidth=\"0.2\"\n        android:pathData=\"M8,6.82v10.36c0,0.79 0.87,1.27 1.54,0.84l8.14,-5.18c0.62,-0.39 0.62,-1.29 0,-1.69L9.54,5.98C8.87,5.55 8,6.03 8,6.82z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_agenda_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M20,13L3,13c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM20,3L3,3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1L21,4c0,-0.55 -0.45,-1 -1,-1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_view_grid_24.xml",
    "content": "<!-- drawable/view_grid.xml -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M3,11H11V3H3M3,21H11V13H3M13,21H21V13H13M13,3V11H21V3\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_off_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_off_24_a50.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_off_24_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_volume_off_24_a50\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/ic_volume_off_24\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_up_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_up_24_a50.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillAlpha=\"0.5\"\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_volume_up_24_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_volume_up_24_a50\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/ic_volume_up_24\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/ic_warning.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"@android:color/white\"\n      android:pathData=\"M4.47,21h15.06c1.54,0 2.5,-1.67 1.73,-3L13.73,4.99c-0.77,-1.33 -2.69,-1.33 -3.46,0L2.74,18c-0.77,1.33 0.19,3 1.73,3zM12,14c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1zM13,18h-2v-2h2v2z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/launch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:opacity=\"opaque\">\n    <item android:drawable=\"@color/white\"/>\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/launch_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:opacity=\"opaque\">\n    <item android:drawable=\"@color/black\"/>\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/launch_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:opacity=\"opaque\">\n    <item android:drawable=\"@color/grey_900\" />\n    <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@drawable/barinsta_logo\" />\n    </item>\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/lock.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"?attr/colorControlNormal\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#000\"\n        android:pathData=\"M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z M18,8 h-1 L17,6 c0,-2.76 -2.24,-5 -5,-5 S7,3.24 7,6 h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1 v2 L6,8 c-1.1,0 -2,0.9 -2,2 v10 c0,1.1 0.9,2 2,2 h12 c1.1,0 2,-0.9 2,-2 L20,10 c0,-1.1 -0.9,-2 -2,-2z M18,20L6,20L6,10h12v10z\n        M7,6 L8.9,6 v2 h-2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/popup_background_exoplayer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <corners android:radius=\"2dp\" />\n    <solid android:color=\"@color/grey_800\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/pref_list_divider_material.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  Copyright (C) 2015 The Android Open Source Project\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n       http://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n  -->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/grey_600_a20\" />\n    <size\n        android:width=\"1dp\"\n        android:height=\"1dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/recv_basket_animated.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<animated-vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:drawable=\"@drawable/recv_ic_delete\"\n    tools:targetApi=\"lollipop\">\n    <target\n        android:name=\"basket\"\n        android:animation=\"@animator/basket_path\" />\n</animated-vector>"
  },
  {
    "path": "app/src/main/res/drawable/recv_ic_arrow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M15.41,16.09l-4.58,-4.59 4.58,-4.59L14,5.5l-6,6 6,6z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/recv_ic_delete.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:name=\"basket\"\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/drawable/recv_ic_mic.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:fillColor=\"#e12626\"\n        android:pathData=\"M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/rounder_corner_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@android:color/transparent\" />\n    <corners android:radius=\"8dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/rounder_corner_semi_black_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/black_a50\" />\n    <padding\n        android:bottom=\"4dp\"\n        android:left=\"4dp\"\n        android:right=\"4dp\"\n        android:top=\"4dp\" />\n\n    <corners android:radius=\"8dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_oval_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n    <solid android:color=\"@android:color/transparent\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/sl_favourite_24.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/ic_outline_star_plus_24\" android:state_checked=\"false\" />\n    <item android:drawable=\"@drawable/ic_star_check_24\" android:state_checked=\"true\" />\n    <item android:drawable=\"@drawable/ic_outline_star_plus_24\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/speed_text_color_states.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/white_a50\" android:state_enabled=\"false\" />\n    <item android:color=\"@color/white\" android:state_enabled=\"true\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/layout/activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/camera_capture_button\"\n        android:layout_width=\"80dp\"\n        android:layout_height=\"80dp\"\n        android:layout_marginBottom=\"80dp\"\n        android:background=\"@android:color/transparent\"\n        android:elevation=\"2dp\"\n        android:scaleType=\"fitCenter\"\n        android:src=\"@drawable/ic_shutter\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clickable=\"true\"\n        android:focusable=\"true\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/switch_camera\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginStart=\"40dp\"\n        android:layout_marginBottom=\"100dp\"\n        android:background=\"@android:color/transparent\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_round_flip_camera_24\"\n        app:tint=\"@color/white\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/close\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginEnd=\"40dp\"\n        android:layout_marginBottom=\"100dp\"\n        android:background=\"@android:color/transparent\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_close_24\"\n        app:tint=\"@color/white\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_crash_error.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"12dp\"\n        android:paddingLeft=\"12dp\"\n        android:paddingEnd=\"12dp\"\n        android:paddingRight=\"12dp\"\n        android:scrollbars=\"vertical\"\n        android:text=\"@string/crash_descr\"\n        android:textSize=\"16sp\" />\n\n    <LinearLayout\n        style=\"?android:buttonBarStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"8dp\"\n        android:gravity=\"end|center_vertical\">\n\n        <Button\n            android:id=\"@+id/btnCancel\"\n            style=\"?android:buttonBarButtonStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\"\n            android:layout_marginEnd=\"5dp\"\n            android:gravity=\"center\"\n            android:padding=\"8dp\"\n            android:text=\"@string/cancel\"\n            android:textSize=\"16sp\" />\n\n        <Button\n            android:id=\"@+id/btnReport\"\n            style=\"?android:buttonBarButtonStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\"\n            android:gravity=\"center\"\n            android:padding=\"8dp\"\n            android:text=\"@string/report\"\n            android:textSize=\"16sp\" />\n    </LinearLayout>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_directory_select.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"16dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/app_name\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline4\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/message\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\"\n        app:layout_constraintBottom_toTopOf=\"@id/prev_uri\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        app:layout_goneMarginBottom=\"0dp\"\n        tools:text=\"@string/dir_select_permission_revoked_message\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/prev_uri\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"8dp\"\n        android:fontFamily=\"monospace\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/blue_500\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/message2\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/message\"\n        app:layout_goneMarginBottom=\"0dp\"\n        tools:text=\"content://something/something/content/content\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/message2\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/dir_select_message2\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/prev_uri\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.progressindicator.CircularProgressIndicator\n        android:id=\"@+id/loading_indicator\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:indeterminate=\"true\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/select_dir\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/title\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/select_dir\"\n        style=\"@style/Widget.MaterialComponents.Button.OutlinedButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/select_folder\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\".activities.Login\">\n\n    <WebView\n        android:id=\"@+id/webView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        style=\"@style/Widget.AppCompat.ButtonBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <androidx.appcompat.widget.AppCompatButton\n            android:id=\"@+id/cookies\"\n            style=\"@style/Widget.AppCompat.Button.ButtonBar.AlertDialog\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/get_cookies\" />\n\n        <androidx.appcompat.widget.AppCompatButton\n            android:id=\"@+id/refresh\"\n            style=\"@style/Widget.AppCompat.Button.ButtonBar.AlertDialog\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/refresh\" />\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<awais.instagrabber.customviews.InsetsNotifyingCoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/main_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:animateLayoutChanges=\"true\"\n    tools:context=\".activities.MainActivity\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/appBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsingToolbarLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:animateLayoutChanges=\"true\"\n            android:minHeight=\"?attr/actionBarSize\"\n            app:layout_scrollFlags=\"scroll|enterAlwaysCollapsed|exitUntilCollapsed\"\n            app:titleEnabled=\"false\">\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                app:layout_collapseMode=\"pin\"\n                app:title=\"@string/app_name\"\n                tools:menu=\"@menu/main_menu\">\n\n                <com.google.android.material.textfield.TextInputLayout\n                    android:id=\"@+id/search_input_layout\"\n                    style=\"?searchInputStyle\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:visibility=\"gone\"\n                    app:boxStrokeColor=\"@null\"\n                    app:boxStrokeWidth=\"0dp\"\n                    app:boxStrokeWidthFocused=\"0dp\"\n                    app:endIconContentDescription=\"@string/clear\"\n                    app:endIconDrawable=\"@drawable/ic_close_24\"\n                    app:endIconMode=\"custom\"\n                    app:hintEnabled=\"false\"\n                    tools:visibility=\"visible\">\n\n                    <com.google.android.material.textfield.TextInputEditText\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/search\" />\n                </com.google.android.material.textfield.TextInputLayout>\n            </com.google.android.material.appbar.MaterialToolbar>\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/main_nav_host\"\n        android:name=\"awais.instagrabber.customviews.BarinstaNavHostFragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        app:defaultNavHost=\"true\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n    <!--app:layout_behavior=\"@string/hide_bottom_view_on_scroll_behavior\"-->\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/bottomNavView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        app:labelVisibilityMode=\"auto\"\n        tools:menu=\"@menu/bottom_nav_menu\" />\n</awais.instagrabber.customviews.InsetsNotifyingCoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_account_switcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/accounts\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toTopOf=\"@id/add_account_btn\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:itemCount=\"5\"\n        tools:listitem=\"@layout/pref_account_switcher\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/add_account_btn\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/add_account\"\n        app:icon=\"@drawable/ic_add\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/accounts\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_create_backup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"16dp\"\n    android:paddingTop=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingBottom=\"0dp\">\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbExportSettings\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:checked=\"true\"\n        android:text=\"@string/dialog_export_settings\" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbExportLogins\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/dialog_export_accounts\" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbExportFavorites\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/dialog_export_favorites\" />\n\n    <include layout=\"@layout/item_pref_divider\" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbPassword\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/set_password\" />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/passwordField\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"@string/password_no_max\"\n        android:visibility=\"gone\"\n        app:counterEnabled=\"true\"\n        app:counterMaxLength=\"32\"\n        app:endIconMode=\"password_toggle\"\n        tools:visibility=\"visible\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/etPassword\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:autofillHints=\"no\"\n            android:inputType=\"textPassword\"\n            android:maxLength=\"2200\"\n            android:scrollHorizontally=\"false\"\n            tools:text=\"test\" />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <include layout=\"@layout/item_pref_divider\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnSaveTo\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"@string/backup\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_keywords_filter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"16dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:animateLayoutChanges=\"true\"\n        android:paddingTop=\"16dp\">\n\n        <androidx.appcompat.widget.AppCompatEditText\n            android:id=\"@+id/edit_text\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:hint=\"@string/hint_keyword\"\n            android:singleLine=\"true\"\n            app:layout_constraintEnd_toStartOf=\"@id/btnAdd\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <Button\n            android:id=\"@+id/btnAdd\"\n            android:layout_width=\"30dp\"\n            android:layout_height=\"30dp\"\n            android:background=\"@drawable/ic_add\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@id/edit_text\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_constraintBottom_toBottomOf=\"@id/edit_text\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerKeyword\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintTop_toBottomOf=\"@id/btnAdd\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            tools:layout_editor_absoluteX=\"16dp\"\n            tools:listitem=\"@layout/item_keyword\" />\n\n        <androidx.appcompat.widget.AppCompatButton\n            android:id=\"@+id/btnOK\"\n            style=\"@style/Widget.AppCompat.Button.ButtonBar.AlertDialog\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end\"\n            android:text=\"@string/ok\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/recyclerKeyword\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/dialog_opening_post.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"16dp\">\n\n    <ProgressBar\n        android:id=\"@+id/progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:indeterminate=\"true\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"@string/opening_post\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/progress_bar\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_post_layout_preferences.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:animateLayoutChanges=\"true\"\n        android:paddingTop=\"16dp\">\n\n        <androidx.constraintlayout.widget.Guideline\n            android:id=\"@+id/guideline\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            app:layout_constraintGuide_percent=\"0.32\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_layout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/layout_style\"\n            app:layout_constraintBottom_toBottomOf=\"@id/layout_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/layout_toggle\" />\n\n        <!-- Button icons do not appear in preview, but will appear\n            in the app. Check https://github.com/material-components/material-components-android/issues/850 -->\n        <com.google.android.material.button.MaterialButtonToggleGroup\n            android:id=\"@+id/layout_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"0dp\"\n            android:layout_marginBottom=\"8dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/col_count_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:selectionRequired=\"true\"\n            app:singleSelection=\"true\">\n\n            <Button\n                android:id=\"@+id/layout_staggered\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:icon=\"@drawable/ic_dashboard_24\" />\n\n            <Button\n                android:id=\"@+id/layout_grid\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:icon=\"@drawable/ic_view_grid_24\" />\n\n            <Button\n                android:id=\"@+id/layout_linear\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:icon=\"@drawable/ic_view_agenda_24\" />\n        </com.google.android.material.button.MaterialButtonToggleGroup>\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_col_count\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/column_count\"\n            app:layout_constraintBottom_toBottomOf=\"@id/col_count_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/col_count_toggle\" />\n\n        <com.google.android.material.button.MaterialButtonToggleGroup\n            android:id=\"@+id/col_count_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"0dp\"\n            android:layout_marginBottom=\"8dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/show_names_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/layout_toggle\"\n            app:selectionRequired=\"true\"\n            app:singleSelection=\"true\">\n\n            <Button\n                android:id=\"@+id/col_count_two\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/two\" />\n\n            <Button\n                android:id=\"@+id/col_count_three\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/three\" />\n        </com.google.android.material.button.MaterialButtonToggleGroup>\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_show_names_toggle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/show_names\"\n            app:layout_constraintBottom_toBottomOf=\"@id/show_names_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/show_names_toggle\" />\n\n        <com.google.android.material.switchmaterial.SwitchMaterial\n            android:id=\"@+id/show_names_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:checked=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/show_avatar_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/col_count_toggle\"\n            app:showText=\"false\"\n            app:switchPadding=\"0dp\"\n            app:thumbTextPadding=\"0dp\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_show_avatar_toggle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/show_avatars\"\n            app:layout_constraintBottom_toBottomOf=\"@id/show_avatar_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/show_avatar_toggle\" />\n\n        <com.google.android.material.switchmaterial.SwitchMaterial\n            android:id=\"@+id/show_avatar_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:checked=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/avatar_size_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/show_names_toggle\"\n            app:showText=\"false\"\n            app:switchPadding=\"0dp\"\n            app:thumbTextPadding=\"0dp\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_avatar_size\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/avatar_size\"\n            app:layout_constraintBottom_toBottomOf=\"@id/avatar_size_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/avatar_size_toggle\" />\n\n        <com.google.android.material.button.MaterialButtonToggleGroup\n            android:id=\"@+id/avatar_size_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"0dp\"\n            android:layout_marginBottom=\"8dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/corners_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/show_avatar_toggle\"\n            app:selectionRequired=\"true\"\n            app:singleSelection=\"true\">\n\n            <Button\n                android:id=\"@+id/avatar_size_tiny\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"match_parent\"\n                app:icon=\"@drawable/ic_profile_24\" />\n\n            <Button\n                android:id=\"@+id/avatar_size_small\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"match_parent\"\n                app:icon=\"@drawable/ic_profile_40\" />\n\n            <Button\n                android:id=\"@+id/avatar_size_regular\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"match_parent\"\n                app:icon=\"@drawable/ic_profile_48\" />\n        </com.google.android.material.button.MaterialButtonToggleGroup>\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_corners\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/corners\"\n            app:layout_constraintBottom_toBottomOf=\"@id/corners_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/corners_toggle\" />\n\n        <com.google.android.material.button.MaterialButtonToggleGroup\n            android:id=\"@+id/corners_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"0dp\"\n            android:layout_marginBottom=\"8dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/show_gap_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/avatar_size_toggle\"\n            app:selectionRequired=\"true\"\n            app:singleSelection=\"true\">\n\n            <Button\n                android:id=\"@+id/corners_round\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:icon=\"@drawable/ic_rounded_corner_24\" />\n\n            <Button\n                android:id=\"@+id/corners_square\"\n                style=\"@style/Widget.App.Button.OutlinedButton.IconOnly\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:icon=\"@drawable/ic_border_style_flipped_24\" />\n        </com.google.android.material.button.MaterialButtonToggleGroup>\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/label_gap\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"4dp\"\n            android:layout_marginRight=\"6dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:gravity=\"right\"\n            android:text=\"@string/show_grid_gap\"\n            app:layout_constraintBottom_toBottomOf=\"@id/show_gap_toggle\"\n            app:layout_constraintEnd_toStartOf=\"@id/guideline\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/show_gap_toggle\" />\n\n        <com.google.android.material.switchmaterial.SwitchMaterial\n            android:id=\"@+id/show_gap_toggle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:checked=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/show_gap_toggle\"\n            app:layout_constraintStart_toEndOf=\"@id/guideline\"\n            app:layout_constraintTop_toBottomOf=\"@id/corners_toggle\"\n            app:showText=\"false\"\n            app:switchPadding=\"0dp\"\n            app:thumbTextPadding=\"0dp\" />\n\n        <androidx.constraintlayout.widget.Group\n            android:id=\"@+id/staggered_or_grid_options\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:visibility=\"visible\"\n            app:constraint_referenced_ids=\"label_col_count,col_count_toggle,label_show_avatar_toggle,show_avatar_toggle,label_show_names_toggle,show_names_toggle,label_avatar_size,avatar_size_toggle\"\n            tools:visibility=\"visible\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/dialog_post_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?colorSurface\"\n    android:paddingBottom=\"?attr/actionBarSize\"\n    tools:context=\".fragments.PostViewV2Fragment\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/content_root\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <awais.instagrabber.customviews.ProfilePicView\n            android:id=\"@+id/profile_pic\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"12dp\"\n            android:transitionName=\"profile_pic\"\n            app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n            app:layout_constraintEnd_toStartOf=\"@id/title\"\n            app:layout_constraintHorizontal_bias=\"0\"\n            app:layout_constraintHorizontal_chainStyle=\"packed\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:size=\"regular\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:singleLine=\"true\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n            app:layout_constraintEnd_toStartOf=\"@id/options\"\n            app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n            app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n            tools:text=\"Username Username Username\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/subtitle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:singleLine=\"true\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n            app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n            app:layout_constraintEnd_toStartOf=\"@id/options\"\n            app:layout_constraintStart_toStartOf=\"@id/title\"\n            app:layout_constraintTop_toBottomOf=\"@id/title\"\n            tools:text=\"Full name Full name Full name Full name Full name Full name Full name \"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/options\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"0dp\"\n            android:paddingStart=\"8dp\"\n            android:paddingEnd=\"8dp\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n            app:srcCompat=\"@drawable/ic_more_vert_24\"\n            tools:visibility=\"visible\" />\n\n        <androidx.constraintlayout.widget.Barrier\n            android:id=\"@+id/top_barrier\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            app:barrierAllowsGoneWidgets=\"true\"\n            app:barrierDirection=\"bottom\" />\n\n        <FrameLayout\n            android:id=\"@+id/post_container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintBottom_toTopOf=\"@id/buttons_top_barrier\"\n            app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n            tools:layout_height=\"100dp\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/media_counter\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"16dp\"\n            android:layout_marginTop=\"16dp\"\n            android:background=\"@drawable/rounder_corner_semi_black_bg\"\n            android:gravity=\"center\"\n            android:padding=\"8dp\"\n            android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n            android:textColor=\"@android:color/white\"\n            android:visibility=\"gone\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n            tools:text=\"1/5\"\n            tools:visibility=\"visible\" />\n\n        <com.google.android.material.button.MaterialButton\n            android:id=\"@+id/location\"\n            style=\"?borderlessButtonStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"16dp\"\n            android:layout_marginEnd=\"16dp\"\n            android:elevation=\"0dp\"\n            android:ellipsize=\"end\"\n            android:insetTop=\"0dp\"\n            android:insetBottom=\"0dp\"\n            android:maxWidth=\"200dp\"\n            android:maxLines=\"1\"\n            android:minHeight=\"32dp\"\n            android:paddingStart=\"8dp\"\n            android:paddingEnd=\"8dp\"\n            android:textAlignment=\"viewStart\"\n            android:textAllCaps=\"false\"\n            android:textColor=\"@android:color/white\"\n            android:visibility=\"gone\"\n            app:backgroundTint=\"@color/black_a50\"\n            app:elevation=\"0dp\"\n            app:icon=\"@drawable/ic_round_location_on_24\"\n            app:iconSize=\"16dp\"\n            app:iconTint=\"@color/white\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n            app:rippleColor=\"@color/grey_600\"\n            tools:text=\"Location, Location, Location, Location, \"\n            tools:visibility=\"visible\" />\n\n        <include layout=\"@layout/layout_post_view_bottom\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</androidx.core.widget.NestedScrollView>"
  },
  {
    "path": "app/src/main/res/layout/dialog_profilepic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black_a50\">\n\n    <awais.instagrabber.customviews.drawee.ZoomableDraweeView\n        android:id=\"@+id/imageViewer\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <!--<androidx.appcompat.widget.AppCompatTextView-->\n    <!--    android:id=\"@+id/imageInfo\"-->\n    <!--    android:layout_width=\"wrap_content\"-->\n    <!--    android:layout_height=\"wrap_content\"-->\n    <!--    android:background=\"@android:color/black\"-->\n    <!--    android:padding=\"8dp\"-->\n    <!--    android:text=\"Test text\"-->\n    <!--    android:textColor=\"@android:color/white\"-->\n    <!--    android:visibility=\"visible\"-->\n    <!--    tools:visibility=\"visible\" />-->\n\n    <ProgressBar\n        android:id=\"@+id/progressView\"\n        style=\"@android:style/Widget.DeviceDefault.Light.ProgressBar.Large\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:background=\"@android:color/transparent\" />\n\n    <Button\n        android:id=\"@+id/download\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|end\"\n        android:layout_marginRight=\"8dp\"\n        android:layout_marginBottom=\"4dp\"\n        android:backgroundTint=\"@color/black_a50\"\n        android:text=\"@string/action_download\"\n        android:textColor=\"@color/white\"\n        android:visibility=\"gone\"\n        app:icon=\"@drawable/ic_download\"\n        app:iconTint=\"@color/white\"\n        tools:visibility=\"visible\" />\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_restore_backup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"16dp\"\n    android:paddingTop=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingBottom=\"0dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/file_chosen_label\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/file_chosen_label\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body2\"\n        app:layout_constraintBottom_toTopOf=\"@id/file_path\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/file_path\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/file_chosen_label\"\n        tools:text=\"file path file path file path file path file path file path file path file path file path file path file path \" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbSettings\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:checked=\"true\"\n        android:text=\"@string/dialog_export_settings\"\n        app:layout_constraintBottom_toTopOf=\"@id/cbAccounts\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/file_path\" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbAccounts\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/dialog_export_accounts\"\n        app:layout_constraintBottom_toTopOf=\"@id/cbFavorites\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/cbSettings\" />\n\n    <androidx.appcompat.widget.AppCompatCheckBox\n        android:id=\"@+id/cbFavorites\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/dialog_export_favorites\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_password_divider\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/cbAccounts\" />\n\n    <include\n        android:id=\"@+id/top_password_divider\"\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/enter_password_label\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/cbFavorites\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/enter_password_label\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginBottom=\"4dp\"\n        android:text=\"@string/enter_password\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        app:layout_constraintBottom_toTopOf=\"@id/passwordField\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_password_divider\" />\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/passwordField\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"8dp\"\n        android:hint=\"@string/password_no_max\"\n        app:counterEnabled=\"true\"\n        app:counterMaxLength=\"32\"\n        app:endIconMode=\"password_toggle\"\n        app:layout_constraintBottom_toTopOf=\"@id/bottom_password_divider\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/enter_password_label\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/etPassword\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:autofillHints=\"no\"\n            android:inputType=\"textPassword\"\n            android:maxLength=\"2200\"\n            android:scrollHorizontally=\"false\"\n            tools:text=\"test\" />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <include\n        android:id=\"@+id/bottom_password_divider\"\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/btn_restore\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/passwordField\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btn_restore\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"@string/restore\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_password_divider\" />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/password_group\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        app:constraint_referenced_ids=\"top_password_divider,bottom_password_divider,enter_password_label,passwordField\"\n        tools:visibility=\"visible\" />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_time_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"16dp\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/time_settings_title_custom\" />\n\n                <CheckBox\n                    android:id=\"@+id/cbCustomFormat\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\" />\n            </LinearLayout>\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/custom_format_field\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:visibility=\"gone\"\n                app:counterEnabled=\"false\"\n                app:counterMaxLength=\"50\"\n                app:endIconDrawable=\"@drawable/ic_outline_info_24\"\n                app:endIconMode=\"custom\"\n                app:hintEnabled=\"false\"\n                tools:visibility=\"visible\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/custom_format_edit_text\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:autofillHints=\"no\"\n                    android:inputType=\"text\"\n                    android:maxLength=\"50\"\n                    android:padding=\"16dp\"\n                    tools:text=\"test\" />\n\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <FrameLayout\n                android:id=\"@+id/customPanel\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:visibility=\"gone\">\n\n                <include layout=\"@layout/layout_include_custom_format_info\" />\n            </FrameLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"8dp\"\n            android:baselineAligned=\"false\"\n            android:orientation=\"vertical\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/time_settings_title_time_format\" />\n\n                <androidx.appcompat.widget.AppCompatSpinner\n                    android:id=\"@+id/spTimeFormat\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"36dp\"\n                    android:layout_weight=\"1\"\n                    android:entries=\"@array/time_presets\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/time_settings_title_separator\" />\n\n                <androidx.appcompat.widget.AppCompatSpinner\n                    android:id=\"@+id/spSeparator\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"36dp\"\n                    android:layout_weight=\"1\"\n                    android:entries=\"@array/separator_presets\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/time_settings_title_date_format\" />\n\n                <androidx.appcompat.widget.AppCompatSpinner\n                    android:id=\"@+id/spDateFormat\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"36dp\"\n                    android:layout_weight=\"1\"\n                    android:entries=\"@array/date_presets\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <CheckBox\n            android:id=\"@+id/cbSwapTimeDate\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/time_settings_swap_time\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"8dp\"\n            android:baselineAligned=\"false\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"bottom\"\n                android:text=\"@string/time_settings_title_preview\" />\n\n            <TextView\n                android:id=\"@+id/timePreview\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n                tools:text=\"hh:mm:ss a 'on' dd-MM-yyyy\" />\n        </LinearLayout>\n\n        <FrameLayout\n            style=\"@style/Widget.AppCompat.ButtonBar.AlertDialog\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <androidx.appcompat.widget.AppCompatButton\n                android:id=\"@+id/btnConfirm\"\n                style=\"@style/Widget.AppCompat.Button.ButtonBar.AlertDialog\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"end\"\n                android:text=\"@string/confirm\" />\n        </FrameLayout>\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/fragment_collection_posts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/colorSurface\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:contentScrim=\"?attr/toolbarColor\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\">\n\n            <FrameLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:layout_collapseMode=\"parallax\">\n\n                <com.facebook.drawee.view.SimpleDraweeView\n                    android:id=\"@+id/cover\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"200dp\"\n                    app:actualImageScaleType=\"centerCrop\"\n                    tools:background=\"@mipmap/ic_launcher\" />\n\n                <View\n                    android:id=\"@+id/background\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\" />\n            </FrameLayout>\n\n            <androidx.appcompat.widget.Toolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"@null\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/posts\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_comments.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?colorSurface\">\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/replies_container_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:elevation=\"10dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <com.google.android.material.appbar.MaterialToolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toTopOf=\"@id/swipe_refresh_layout\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/comment_field\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/toolbar\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/comments\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            tools:listitem=\"@layout/item_comment\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\n    <!--app:startIconDrawable=\"@drawable/ic_close_24\"-->\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/comment_field\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"4dp\"\n        android:hint=\"@string/comment_hint\"\n        android:visibility=\"gone\"\n        app:counterEnabled=\"false\"\n        app:counterMaxLength=\"2200\"\n        app:endIconDrawable=\"@drawable/ic_round_send_24\"\n        app:endIconMode=\"custom\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/swipe_refresh_layout\"\n        tools:visibility=\"visible\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/comment_text\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:autofillHints=\"no\"\n            android:inputType=\"textMultiLine\"\n            android:maxLength=\"2200\"\n            android:scrollHorizontally=\"false\"\n            tools:text=\"test\" />\n\n    </com.google.android.material.textfield.TextInputLayout>\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_direct_messages_inbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipeRefreshLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"?attr/colorSurface\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/inbox_list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:paddingBottom=\"?attr/actionBarSize\"\n            tools:listitem=\"@layout/layout_dm_inbox_item\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_direct_messages_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusableInTouchMode=\"true\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@null\"\n        android:elevation=\"0dp\"\n        app:elevation=\"0dp\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"scroll\">\n\n            <androidx.constraintlayout.widget.ConstraintLayout\n                android:id=\"@+id/settings_parent\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\">\n\n                <com.google.android.material.textfield.TextInputLayout\n                    android:id=\"@+id/title_edit_input_layout\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:animateLayoutChanges=\"true\"\n                    android:hint=\"@string/title\"\n                    android:visibility=\"gone\"\n                    app:boxBackgroundColor=\"@android:color/transparent\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"parent\"\n                    app:suffixText=\"@string/save\"\n                    app:suffixTextColor=\"@color/blue_600\"\n                    tools:visibility=\"visible\">\n\n                    <com.google.android.material.textfield.TextInputEditText\n                        android:id=\"@+id/title_edit\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:autofillHints=\"no\"\n                        android:inputType=\"text\"\n                        android:maxLength=\"2200\"\n                        android:scrollHorizontally=\"false\"\n                        tools:text=\"test\" />\n\n                </com.google.android.material.textfield.TextInputLayout>\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/mute_messages_label\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"0dp\"\n                    android:background=\"?selectableItemBackground\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:text=\"@string/mute_messages\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"?android:textColorPrimary\"\n                    app:layout_constraintBottom_toBottomOf=\"@id/mute_messages\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"@id/mute_messages\" />\n\n                <com.google.android.material.switchmaterial.SwitchMaterial\n                    android:id=\"@+id/mute_messages\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:layout_marginEnd=\"8dp\"\n                    android:paddingStart=\"0dp\"\n                    android:paddingEnd=\"8dp\"\n                    app:layout_constraintBottom_toTopOf=\"@id/mute_mentions\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintTop_toBottomOf=\"@id/title_edit_input_layout\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/mute_mentions_label\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"0dp\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:text=\"@string/mute_mentions\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"?android:textColorPrimary\"\n                    app:layout_constraintBottom_toBottomOf=\"@id/mute_mentions\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"@id/mute_mentions\" />\n\n                <com.google.android.material.switchmaterial.SwitchMaterial\n                    android:id=\"@+id/mute_mentions\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:layout_marginEnd=\"8dp\"\n                    android:paddingStart=\"0dp\"\n                    android:paddingEnd=\"8dp\"\n                    app:layout_constraintBottom_toTopOf=\"@id/approval_required\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintTop_toBottomOf=\"@id/mute_messages\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/approval_required_label\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"0dp\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:text=\"@string/approval_required_for_new_members\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"?android:textColorPrimary\"\n                    app:layout_constraintBottom_toBottomOf=\"@id/approval_required\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"@id/approval_required\" />\n\n                <com.google.android.material.switchmaterial.SwitchMaterial\n                    android:id=\"@+id/approval_required\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:layout_marginEnd=\"8dp\"\n                    android:paddingStart=\"0dp\"\n                    android:paddingEnd=\"8dp\"\n                    app:layout_constraintBottom_toTopOf=\"@id/leave\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintTop_toBottomOf=\"@id/mute_mentions\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/leave\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:background=\"?selectableItemBackground\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingTop=\"12dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:paddingBottom=\"12dp\"\n                    android:text=\"@string/dms_action_leave\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"@color/red_600\"\n                    app:layout_constraintBottom_toTopOf=\"@id/end\"\n                    app:layout_constraintTop_toBottomOf=\"@id/approval_required\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/end\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:background=\"?selectableItemBackground\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingTop=\"12dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:paddingBottom=\"12dp\"\n                    android:text=\"@string/dms_action_end\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"@color/red_600\"\n                    app:layout_constraintBottom_toTopOf=\"@id/pending_members_header\"\n                    app:layout_constraintTop_toBottomOf=\"@id/leave\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/pending_members_header\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingLeft=\"16dp\"\n                    android:paddingTop=\"16dp\"\n                    android:paddingRight=\"16dp\"\n                    android:paddingBottom=\"8dp\"\n                    android:text=\"@string/requests\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n                    android:textColor=\"?colorAccent\"\n                    android:textStyle=\"bold\"\n                    app:layout_constraintBottom_toTopOf=\"@id/pending_members\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toBottomOf=\"@id/end\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/pending_members_admin_only\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingLeft=\"16dp\"\n                    android:paddingRight=\"16dp\"\n                    android:text=\"@string/admins_only\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n                    app:layout_constraintBaseline_toBaselineOf=\"@id/pending_members_header\"\n                    app:layout_constraintEnd_toEndOf=\"parent\" />\n\n                <androidx.recyclerview.widget.RecyclerView\n                    android:id=\"@+id/pending_members\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    app:layout_constraintBottom_toTopOf=\"@id/add_members\"\n                    app:layout_constraintTop_toBottomOf=\"@id/pending_members_header\"\n                    tools:itemCount=\"2\"\n                    tools:listitem=\"@layout/layout_dm_user_item\" />\n\n                <androidx.appcompat.widget.AppCompatTextView\n                    android:id=\"@+id/add_members\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4dp\"\n                    android:background=\"?selectableItemBackground\"\n                    android:gravity=\"center_vertical\"\n                    android:paddingStart=\"16dp\"\n                    android:paddingTop=\"12dp\"\n                    android:paddingEnd=\"16dp\"\n                    android:paddingBottom=\"12dp\"\n                    android:text=\"@string/add_members\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n                    android:textColor=\"?android:textColorPrimary\"\n                    app:drawableStartCompat=\"@drawable/ic_add\"\n                    app:drawableTint=\"?android:textColorPrimary\"\n                    app:layout_constraintBottom_toBottomOf=\"parent\"\n                    app:layout_constraintTop_toBottomOf=\"@id/pending_members\" />\n\n                <androidx.constraintlayout.widget.Group\n                    android:id=\"@+id/pending_members_group\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"0dp\"\n                    android:visibility=\"gone\"\n                    app:constraint_referenced_ids=\"pending_members,pending_members_admin_only,pending_members_header\"\n                    tools:visibility=\"visible\" />\n\n                <androidx.constraintlayout.widget.Group\n                    android:id=\"@+id/group_settings\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"0dp\"\n                    app:constraint_referenced_ids=\"title_edit_input_layout, mute_mentions_label, mute_mentions, leave, end, add_members, approval_required, approval_required_label\" />\n            </androidx.constraintlayout.widget.ConstraintLayout>\n\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/users\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n        tools:listitem=\"@layout/layout_dm_user_item\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_direct_messages_thread.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<awais.instagrabber.customviews.InsetsAnimationLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:orientation=\"vertical\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/chats\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_weight=\"1\"\n        android:scrollbars=\"none\"\n        tools:listitem=\"@layout/layout_dm_base\" />\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/input_holder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <androidx.constraintlayout.widget.Barrier\n            android:id=\"@+id/chats_barrier\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            app:barrierDirection=\"bottom\" />\n\n        <View\n            android:id=\"@+id/reply_bg\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            android:background=\"@drawable/bg_input\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toEndOf=\"@id/input_bg\"\n            app:layout_constraintStart_toStartOf=\"@id/input_bg\"\n            app:layout_constraintTop_toTopOf=\"@id/reply_info\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/reply_info\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:paddingStart=\"16dp\"\n            android:paddingTop=\"8dp\"\n            android:paddingEnd=\"16dp\"\n            android:paddingBottom=\"4dp\"\n            android:singleLine=\"true\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toTopOf=\"@id/reply_preview_text\"\n            app:layout_constraintEnd_toStartOf=\"@id/reply_preview_image\"\n            app:layout_constraintStart_toStartOf=\"@id/input_bg\"\n            app:layout_constraintTop_toBottomOf=\"@id/chats_barrier\"\n            tools:text=\"Replying to yourself\"\n            tools:visibility=\"gone\" />\n\n        <androidx.emoji.widget.EmojiAppCompatTextView\n            android:id=\"@+id/reply_preview_text\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:paddingStart=\"16dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingEnd=\"16dp\"\n            android:paddingBottom=\"8dp\"\n            android:singleLine=\"true\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toTopOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toStartOf=\"@id/reply_preview_image\"\n            app:layout_constraintStart_toStartOf=\"@id/input_bg\"\n            app:layout_constraintTop_toBottomOf=\"@id/reply_info\"\n            app:layout_goneMarginTop=\"8dp\"\n            tools:text=\"Post\"\n            tools:visibility=\"gone\" />\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/reply_preview_image\"\n            android:layout_width=\"@dimen/dm_inbox_avatar_size_small\"\n            android:layout_height=\"@dimen/dm_inbox_avatar_size_small\"\n            android:layout_marginEnd=\"8dp\"\n            android:visibility=\"gone\"\n            app:actualImageScaleType=\"centerCrop\"\n            app:layout_constraintBottom_toBottomOf=\"@id/reply_preview_text\"\n            app:layout_constraintEnd_toStartOf=\"@id/reply_cancel\"\n            app:layout_constraintStart_toEndOf=\"@id/reply_preview_text\"\n            app:layout_constraintTop_toTopOf=\"@id/reply_info\"\n            tools:background=\"@mipmap/ic_launcher\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/reply_cancel\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_marginEnd=\"12dp\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/reply_preview_text\"\n            app:layout_constraintEnd_toEndOf=\"@id/input_bg\"\n            app:layout_constraintStart_toEndOf=\"@id/reply_preview_image\"\n            app:layout_constraintTop_toTopOf=\"@id/reply_info\"\n            app:srcCompat=\"@drawable/ic_close_24\"\n            tools:visibility=\"gone\" />\n\n        <!--<androidx.constraintlayout.widget.Group-->\n        <!--    android:id=\"@+id/reply_group\"-->\n        <!--    android:layout_width=\"0dp\"-->\n        <!--    android:layout_height=\"0dp\"-->\n        <!--    android:visibility=\"gone\"-->\n        <!--    app:constraint_referenced_ids=\"reply_bg,reply_cancel,reply_info,reply_item_type,reply_preview\"-->\n        <!--    tools:visibility=\"visible\" />-->\n\n        <View\n            android:id=\"@+id/input_bg\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            android:layout_marginStart=\"4dp\"\n            android:layout_marginEnd=\"4dp\"\n            android:background=\"@drawable/bg_input\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input\"\n            app:layout_constraintEnd_toStartOf=\"@id/send\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/input\"\n            tools:visibility=\"visible\" />\n\n        <com.google.android.material.button.MaterialButton\n            android:id=\"@+id/emoji_toggle\"\n            style=\"@style/Widget.MaterialComponents.Button.Icon.NoInsets\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"2dp\"\n            android:background=\"@android:color/transparent\"\n            android:scrollbars=\"none\"\n            android:visibility=\"gone\"\n            app:icon=\"@drawable/ic_face_24\"\n            app:iconGravity=\"textStart\"\n            app:iconSize=\"24dp\"\n            app:iconTint=\"@color/grey_700\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toStartOf=\"@id/input\"\n            app:layout_constraintStart_toStartOf=\"@id/input_bg\"\n            app:layout_constraintTop_toTopOf=\"@id/input\"\n            app:rippleColor=\"@color/grey_500\"\n            app:shapeAppearanceOverlay=\"@style/ShapeAppearanceOverlay.App.Button.Circle\"\n            app:strokeColor=\"@color/black\"\n            app:strokeWidth=\"1dp\"\n            tools:visibility=\"visible\" />\n\n        <awais.instagrabber.customviews.KeyNotifyingEmojiEditText\n            android:id=\"@+id/input\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"4dp\"\n            android:background=\"@android:color/transparent\"\n            android:hint=\"@string/message\"\n            android:paddingTop=\"12dp\"\n            android:paddingBottom=\"12dp\"\n            android:textColor=\"?dmInputTextColor\"\n            android:textColorHint=\"@color/grey_500\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toStartOf=\"@id/gif\"\n            app:layout_constraintStart_toEndOf=\"@id/emoji_toggle\"\n            app:layout_constraintTop_toBottomOf=\"@id/reply_preview_text\"\n            app:layout_goneMarginBottom=\"4dp\"\n            app:layout_goneMarginEnd=\"24dp\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatImageButton\n            android:id=\"@+id/gif\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"0dp\"\n            android:background=\"@android:color/transparent\"\n            android:scaleType=\"fitCenter\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toStartOf=\"@id/camera\"\n            app:layout_constraintStart_toEndOf=\"@id/input\"\n            app:layout_constraintTop_toTopOf=\"@id/input\"\n            app:srcCompat=\"@drawable/ic_round_gif_24\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatImageButton\n            android:id=\"@+id/camera\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"0dp\"\n            android:layout_marginStart=\"4dp\"\n            android:background=\"@android:color/transparent\"\n            android:paddingStart=\"4dp\"\n            android:paddingEnd=\"4dp\"\n            android:scaleType=\"fitCenter\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toStartOf=\"@id/gallery\"\n            app:layout_constraintStart_toEndOf=\"@id/gif\"\n            app:layout_constraintTop_toTopOf=\"@id/input\"\n            app:srcCompat=\"@drawable/ic_camera_24\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatImageButton\n            android:id=\"@+id/gallery\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"0dp\"\n            android:layout_marginStart=\"4dp\"\n            android:layout_marginEnd=\"16dp\"\n            android:background=\"@android:color/transparent\"\n            android:paddingStart=\"4dp\"\n            android:paddingEnd=\"4dp\"\n            android:scaleType=\"fitCenter\"\n            android:src=\"@drawable/ic_image_24\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toStartOf=\"@id/send\"\n            app:layout_constraintStart_toEndOf=\"@id/camera\"\n            app:layout_constraintTop_toTopOf=\"@id/input\"\n            tools:visibility=\"visible\" />\n\n        <awais.instagrabber.customviews.RecordView\n            android:id=\"@+id/record_view\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            android:visibility=\"gone\"\n            app:counter_time_color=\"?attr/colorOnPrimary\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toEndOf=\"@id/input_bg\"\n            app:layout_constraintStart_toStartOf=\"@id/input\"\n            app:layout_constraintTop_toBottomOf=\"@id/chats_barrier\"\n            app:slide_to_cancel_arrow=\"@drawable/recv_ic_arrow\"\n            app:slide_to_cancel_arrow_color=\"?attr/colorOnPrimary\"\n            app:slide_to_cancel_bounds=\"0dp\"\n            app:slide_to_cancel_margin_right=\"16dp\"\n            app:slide_to_cancel_text=\"@string/slide_to_cancel\"\n            app:slide_to_cancel_text_color=\"?attr/colorOnPrimary\"\n            tools:visibility=\"visible\" />\n\n        <awais.instagrabber.customviews.RecordButton\n            android:id=\"@+id/send\"\n            style=\"@style/Widget.MaterialComponents.Button.Icon.NoInsets\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:visibility=\"gone\"\n            app:backgroundTint=\"@color/blue_900\"\n            app:elevation=\"4dp\"\n            app:icon=\"@drawable/avd_mic_to_send_anim\"\n            app:iconGravity=\"textStart\"\n            app:iconSize=\"24dp\"\n            app:iconTint=\"@color/white\"\n            app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@id/input_bg\"\n            app:shapeAppearanceOverlay=\"@style/ShapeAppearanceOverlay.App.Button.Circle\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/accept_pending_request_question\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingTop=\"16dp\"\n            android:paddingBottom=\"8dp\"\n            android:text=\"@string/accept_request_from_user\"\n            android:textAlignment=\"center\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toTopOf=\"@id/decline\"\n            app:layout_constraintTop_toBottomOf=\"@id/chats_barrier\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/decline\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:text=\"@string/decline\"\n            android:textAlignment=\"center\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n            android:textColor=\"@color/red_500\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toStartOf=\"@id/accept\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/accept_pending_request_question\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/accept\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:text=\"@string/accept\"\n            android:textAlignment=\"center\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@id/decline\"\n            app:layout_constraintTop_toBottomOf=\"@id/accept_pending_request_question\"\n            tools:visibility=\"gone\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <awais.instagrabber.customviews.emoji.EmojiPicker\n        android:id=\"@+id/emoji_picker\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"250dp\"\n        android:layout_marginBottom=\"-250dp\"\n        android:alpha=\"0\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n</awais.instagrabber.customviews.InsetsAnimationLinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_direct_pending_inbox.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipeRefreshLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"?attr/colorSurface\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/pending_inbox_list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:paddingBottom=\"?attr/actionBarSize\"\n            tools:listitem=\"@layout/layout_dm_inbox_item\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/empty\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"16dp\"\n        android:text=\"@string/no_pending_requests\"\n        android:textAlignment=\"center\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:visibility=\"gone\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_discover.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/swipe_refresh_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n    tools:context=\".fragments.DiscoverFragment\">\n\n    <awais.instagrabber.customviews.PostsRecyclerView\n        android:id=\"@+id/posts\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_favorites.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/favorite_list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"?attr/actionBarSize\"\n    tools:listitem=\"@layout/item_search_result\" />"
  },
  {
    "path": "app/src/main/res/layout/fragment_feed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleEnabled=\"false\">\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/header\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                android:clipToPadding=\"false\"\n                android:orientation=\"horizontal\"\n                app:layout_constraintBottom_toTopOf=\"@id/feed_swipe_refresh_layout\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                tools:layoutManager=\"androidx.recyclerview.widget.LinearLayoutManager\"\n                tools:listitem=\"@layout/item_highlight\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                app:layout_collapseMode=\"pin\"\n                app:title=\"@string/feed\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/feed_swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:paddingBottom=\"?attr/actionBarSize\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/feed_recycler_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            tools:listitem=\"@layout/item_feed_grid\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_filters.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:background=\"@color/black\">\n\n    <jp.co.cyberagent.android.gpuimage.GPUImageView\n        android:id=\"@+id/preview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:transitionName=\"preview\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/top_barrier\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"top\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/filters\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/cancel\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:layoutManager=\"androidx.recyclerview.widget.LinearLayoutManager\"\n        tools:listitem=\"@layout/item_filter\"\n        tools:orientation=\"horizontal\"\n        tools:visibility=\"visible\" />\n\n    <ScrollView\n        android:id=\"@+id/tune_controls_wrapper\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/cancel\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/cancel\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/reset\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/reset\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/apply\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/apply\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_followers_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/swipeRefreshLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".fragments.FollowViewerFragment\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvFollow\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:paddingStart=\"8dp\"\n        android:paddingLeft=\"8dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingRight=\"8dp\"\n        tools:listitem=\"@layout/item_follow\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_hashtag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleEnabled=\"false\">\n\n            <include\n                android:id=\"@+id/header\"\n                layout=\"@layout/layout_hashtag_details\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"?attr/actionBarSize\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/posts\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_image_edit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black\">\n\n    <awais.instagrabber.customviews.drawee.ZoomableDraweeView\n        android:id=\"@+id/preview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:transitionName=\"preview\"\n        android:visibility=\"gone\"\n        app:actualImageScaleType=\"fitCenter\"\n        tools:background=\"@mipmap/ic_launcher\"\n        tools:visibility=\"visible\" />\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/fragment_container_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/bottom_barrier\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/bottom_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"top\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/black_a50\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/bottom_barrier\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"Cancel\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/done\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"Done\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/crop\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"48dp\"\n        android:scaleType=\"centerInside\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/tune\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\"\n        app:srcCompat=\"@drawable/ic_round_crop_24\"\n        app:tint=\"@color/image_edit_tab_tint\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/tune\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"48dp\"\n        android:scaleType=\"centerInside\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/filters\"\n        app:layout_constraintStart_toEndOf=\"@id/crop\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\"\n        app:srcCompat=\"@drawable/ic_round_tune_24\"\n        app:tint=\"@color/image_edit_tab_tint\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/filters\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"48dp\"\n        android:scaleType=\"centerInside\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/tune\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\"\n        app:srcCompat=\"@drawable/ic_photo_filter\"\n        app:tint=\"@color/image_edit_tab_tint\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/crop_cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/cancel\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/crop_reset\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/crop_reset\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/reset\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/crop_done\"\n        app:layout_constraintStart_toEndOf=\"@id/crop_cancel\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/crop_done\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:foreground=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:text=\"@string/crop\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/crop_reset\"\n        app:layout_constraintTop_toBottomOf=\"@id/bottom_barrier\" />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/result_bottom_controls\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:visibility=\"gone\"\n        app:constraint_referenced_ids=\"cancel, done, crop, tune, filters\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/crop_bottom_controls\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:visibility=\"gone\"\n        app:constraint_referenced_ids=\"crop_cancel, crop_done, crop_reset\"\n        tools:visibility=\"gone\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_likes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.LinearLayoutCompat xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipeRefreshLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/rvLikes\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            tools:listitem=\"@layout/item_follow\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.appcompat.widget.LinearLayoutCompat>"
  },
  {
    "path": "app/src/main/res/layout/fragment_location.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/colorSurface\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleEnabled=\"false\">\n\n            <include\n                android:id=\"@+id/header\"\n                layout=\"@layout/layout_location_details\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"?attr/actionBarSize\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/posts\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_notifications_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/swipeRefreshLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"?attr/actionBarSize\"\n    tools:context=\".fragments.NotificationsViewerFragment\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvComments\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        tools:listitem=\"@layout/item_notification\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/colorSurface\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\"\n            app:titleEnabled=\"false\">\n\n            <include\n                android:id=\"@+id/header\"\n                layout=\"@layout/layout_profile_details\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"?attr/actionBarSize\" />\n\n            <com.google.android.material.appbar.MaterialToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"?attr/toolbarColor\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:paddingBottom=\"?attr/actionBarSize\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/posts_recycler_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            tools:listitem=\"@layout/item_feed_photo\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n\n    <LinearLayout\n        android:id=\"@+id/privatePage\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"top\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:visibility=\"gone\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n        tools:visibility=\"visible\">\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/privatePage1\"\n            android:layout_width=\"@dimen/private_page_size\"\n            android:layout_height=\"@dimen/private_page_size\"\n            android:visibility=\"gone\"\n            app:srcCompat=\"@drawable/lock\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/privatePage2\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center\"\n            android:text=\"@string/priv_acc\"\n            android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n            android:visibility=\"gone\"\n            tools:visibility=\"visible\" />\n    </LinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_saved.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/swipe_refresh_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n    tools:context=\".fragments.SavedViewerFragment\">\n\n    <awais.instagrabber.customviews.PostsRecyclerView\n        android:id=\"@+id/posts\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_saved_collections.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/colorSurface\">\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/topics_recycler_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layoutManager=\"androidx.recyclerview.widget.GridLayoutManager\"\n            app:spanCount=\"2\"\n            tools:itemCount=\"10\"\n            tools:listitem=\"@layout/item_discover_topic\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.LinearLayoutCompat xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <com.google.android.material.tabs.TabLayout\n        android:id=\"@+id/tab_layout\"\n        style=\"@style/Widget.MaterialComponents.TabLayout.RegularCaps\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n    <androidx.viewpager2.widget.ViewPager2\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n</androidx.appcompat.widget.LinearLayoutCompat>"
  },
  {
    "path": "app/src/main/res/layout/fragment_story_list_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/swipeRefreshLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".fragments.StoryListViewerFragment\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rvStories\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        tools:listitem=\"@layout/item_notification\" />\n</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_story_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <FrameLayout\n        android:id=\"@+id/story_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.google.android.exoplayer2.ui.PlayerView\n            android:id=\"@+id/playerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:visibility=\"gone\"\n            app:auto_show=\"true\"\n            app:controller_layout_id=\"@layout/layout_controls\"\n            app:repeat_toggle_modes=\"none\" />\n\n        <awais.instagrabber.customviews.drawee.ZoomableDraweeView\n            android:id=\"@+id/imageViewer\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:visibility=\"gone\" />\n\n        <ProgressBar\n            android:id=\"@+id/progressView\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:layout_gravity=\"center\"\n            android:visibility=\"gone\" />\n\n    </FrameLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/storiesList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:clipToPadding=\"false\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/buttons_barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"bottom\"\n        app:layout_constraintTop_toBottomOf=\"@id/story_container\"\n        app:layout_constraintBottom_toTopOf=\"@id/btnBackward\"\n        app:constraint_referenced_ids=\"story_container\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnBackward\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/exo_ic_skip_previous\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnShare\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnShare\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"?attr/actionModeShareDrawable\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/stickers\"\n        app:layout_constraintStart_toEndOf=\"@id/btnBackward\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/stickers\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_sticker_curved_outlines\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/list_toggle\"\n        app:layout_constraintStart_toEndOf=\"@id/btnShare\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/list_toggle\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_story_viewer_list\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnDownload\"\n        app:layout_constraintStart_toEndOf=\"@id/stickers\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnDownload\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_download\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnReply\"\n        app:layout_constraintStart_toEndOf=\"@id/list_toggle\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnReply\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_round_send_24\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnForward\"\n        app:layout_constraintStart_toEndOf=\"@id/btnDownload\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnForward\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n\n        android:layout_width=\"0dp\"\n        android:layout_height=\"48dp\"\n        android:enabled=\"false\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/exo_ic_skip_next\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/ic_read_button_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/btnReply\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_barrier\"\n        app:rippleColor=\"@color/grey_300\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_topic_posts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/colorSurface\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:contentScrim=\"?attr/toolbarColor\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\">\n\n            <FrameLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:layout_collapseMode=\"parallax\">\n\n                <com.facebook.drawee.view.SimpleDraweeView\n                    android:id=\"@+id/cover\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"200dp\"\n                    app:actualImageScaleType=\"centerCrop\"\n                    tools:background=\"@mipmap/ic_launcher\" />\n\n                <View\n                    android:id=\"@+id/background\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\" />\n            </FrameLayout>\n\n            <androidx.appcompat.widget.Toolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                android:background=\"@null\"\n                app:layout_collapseMode=\"pin\" />\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout\n        android:id=\"@+id/swipe_refresh_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <awais.instagrabber.customviews.PostsRecyclerView\n            android:id=\"@+id/posts\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\" />\n    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_user_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusableInTouchMode=\"true\">\n\n    <com.google.android.material.chip.ChipGroup\n        android:id=\"@+id/group\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/bg_user_search_input\"\n        android:padding=\"10dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/results\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <androidx.appcompat.widget.AppCompatEditText\n            android:id=\"@+id/search\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"48dp\"\n            android:background=\"@android:color/transparent\"\n            android:gravity=\"center_vertical\"\n            android:hint=\"@string/search\"\n            android:importantForAutofill=\"no\"\n            android:inputType=\"textNoSuggestions\"\n            android:maxLines=\"1\"\n            tools:text=\"test\" />\n    </com.google.android.material.chip.ChipGroup>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/results\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/done\"\n        app:layout_constraintTop_toBottomOf=\"@id/group\" />\n\n    <androidx.appcompat.widget.AppCompatButton\n        android:id=\"@+id/done\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:backgroundTint=\"?attr/colorPrimary\"\n        android:text=\"@string/done\"\n        android:textColor=\"?attr/colorOnPrimary\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/results\"\n        tools:layout_editor_absoluteX=\"8dp\"\n        tools:visibility=\"visible\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/header_follow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginBottom=\"4dp\"\n    android:background=\"?android:selectableItemBackground\"\n    tools:viewBindingIgnore=\"true\">\n\n    <TextView\n        android:id=\"@android:id/text1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"?android:attr/listPreferredItemHeightSmall\"\n        android:paddingStart=\"?android:attr/listPreferredItemPaddingStart\"\n        android:paddingLeft=\"?android:attr/listPreferredItemPaddingLeft\"\n        android:paddingEnd=\"?android:attr/listPreferredItemPaddingEnd\"\n        android:paddingRight=\"?android:attr/listPreferredItemPaddingRight\"\n        android:text=\"@string/app_name\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\"\n        android:textStyle=\"bold\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/collapsingArrow\"\n        android:layout_width=\"32dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignTop=\"@android:id/text1\"\n        android:layout_alignBottom=\"@android:id/text1\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_marginEnd=\"3dp\"\n        android:layout_marginRight=\"3dp\"\n        android:contentDescription=\"@null\"\n        app:srcCompat=\"@drawable/ic_keyboard_arrow_down_24\" />\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_comment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:clickable=\"true\"\n    android:descendantFocusability=\"afterDescendants\"\n    android:focusable=\"true\"\n    android:foreground=\"?selectableItemBackground\"\n    android:paddingStart=\"16dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingEnd=\"16dp\"\n    android:paddingBottom=\"4dp\">\n\n    <androidx.constraintlayout.widget.Guideline\n        android:id=\"@+id/profile_pic_guideline\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:orientation=\"vertical\"\n        app:layout_constraintGuide_begin=\"56dp\" />\n\n    <awais.instagrabber.customviews.ProfilePicView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"16dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintEnd_toStartOf=\"@id/profile_pic_guideline\"\n        app:layout_constraintHorizontal_bias=\"1\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:size=\"small\" />\n\n    <awais.instagrabber.customviews.UsernameTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        android:textColor=\"?android:textColorPrimary\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toTopOf=\"@id/date\"\n        app:layout_constraintEnd_toStartOf=\"@id/options\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic_guideline\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"username username username\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/options\"\n        android:layout_width=\"32dp\"\n        android:layout_height=\"32dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:padding=\"4dp\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_more_vert_24\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/date\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"8dp\"\n        android:alpha=\"0.8\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        app:layout_constraintBottom_toTopOf=\"@id/comment\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/username\"\n        app:layout_constraintTop_toBottomOf=\"@id/username\"\n        tools:text=\"long date...................\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/comment\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        app:layout_constraintBottom_toTopOf=\"@id/comment_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/username\"\n        app:layout_constraintTop_toBottomOf=\"@id/date\"\n        tools:text=\"comment comment comment comment comment comment comment comment comment comment comment comment\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/comment_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"bottom\" />\n\n    <awais.instagrabber.customviews.TextViewDrawableSize\n        android:id=\"@+id/likes\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"32dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:clickable=\"true\"\n        android:drawablePadding=\"4dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle2\"\n        app:compoundDrawableHeight=\"16dp\"\n        app:compoundDrawableWidth=\"16dp\"\n        app:drawableTint=\"@color/red_800\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/replies\"\n        app:layout_constraintHorizontal_bias=\"0\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintStart_toStartOf=\"@id/comment\"\n        app:layout_constraintTop_toBottomOf=\"@id/comment_barrier\"\n        tools:text=\"99999\" />\n\n    <awais.instagrabber.customviews.TextViewDrawableSize\n        android:id=\"@+id/replies\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"32dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:clickable=\"true\"\n        android:drawablePadding=\"4dp\"\n        android:focusable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle2\"\n        app:compoundDrawableHeight=\"16dp\"\n        app:compoundDrawableWidth=\"16dp\"\n        app:drawableStartCompat=\"@drawable/ic_round_mode_comment_24\"\n        app:drawableTint=\"@color/grey_500\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/likes\"\n        app:layout_constraintTop_toBottomOf=\"@id/comment_barrier\"\n        tools:text=\"9999\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_dir_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?attr/selectableItemBackground\"\n    android:minHeight=\"56dp\">\n\n    <ImageView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:paddingStart=\"16dp\"\n        android:paddingTop=\"8dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingBottom=\"8dp\"\n        tools:ignore=\"ContentDescription\" />\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:textAppearance=\"?attr/textAppearanceSubtitle1\"\n        tools:text=\"Line line\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_discover_topic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/cover\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintDimensionRatio=\"H,1:1\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundedCornerRadius=\"5dp\"\n        app:viewAspectRatio=\"1\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <View\n        android:id=\"@+id/background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHeight_percent=\"0.5\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"bottom\"\n        android:padding=\"8dp\"\n        android:textStyle=\"bold\"\n        app:autoSizeTextType=\"uniform\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHeight_percent=\"0.3\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        tools:text=\"Title\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_emoji_grid.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <awais.instagrabber.customviews.SquareImageView\n        android:id=\"@+id/image\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/indicator\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/image\"\n        app:layout_constraintEnd_toEndOf=\"@id/image\"\n        app:layout_constraintStart_toStartOf=\"@id/image\"\n        app:layout_constraintTop_toTopOf=\"@id/image\"\n        app:srcCompat=\"@drawable/bg_indicator\"\n        app:tint=\"@color/grey_800\"\n        tools:visibility=\"visible\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_fav_section_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.AppCompatTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"16dp\"\n    android:paddingTop=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingBottom=\"8dp\"\n    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n    android:textColor=\"?colorAccent\"\n    android:textStyle=\"bold\"\n    tools:text=\"HEADERS\" />"
  },
  {
    "path": "app/src/main/res/layout/item_feed_grid.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:animateLayoutChanges=\"true\"\n    android:background=\"@drawable/rounder_corner_bg\"\n    android:foreground=\"?android:selectableItemBackground\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/postImage\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.constraintlayout.widget.Barrier\n            android:id=\"@+id/profile_pic_top_barrier\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            app:barrierDirection=\"bottom\" />\n\n        <androidx.constraintlayout.widget.Barrier\n            android:id=\"@+id/profile_pic_bottom_barrier\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            app:barrierDirection=\"bottom\" />\n\n        <awais.instagrabber.customviews.CircularImageView\n            android:id=\"@+id/profile_pic\"\n            android:layout_width=\"40dp\"\n            android:layout_height=\"40dp\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            android:elevation=\"4dp\"\n            app:layout_constraintBottom_toBottomOf=\"@id/profile_pic_bottom_barrier\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/profile_pic_top_barrier\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            android:layout_marginEnd=\"8dp\"\n            android:background=\"@drawable/rounder_corner_semi_black_bg\"\n            android:ellipsize=\"none\"\n            android:fadingEdgeLength=\"40dp\"\n            android:requiresFadingEdge=\"horizontal\"\n            android:singleLine=\"true\"\n            android:textColor=\"@color/white\"\n            android:textStyle=\"bold\"\n            app:layout_constrainedWidth=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"@id/profile_pic_bottom_barrier\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0\"\n            app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n            app:layout_constraintTop_toTopOf=\"@id/profile_pic_top_barrier\"\n            tools:text=\"Full name Full name Full name Full name Full name Full name Full name \" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/downloaded\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_margin=\"8dp\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:srcCompat=\"@drawable/ic_download_circle_24\"\n            app:tint=\"@color/green_A400\"\n            tools:visibility=\"visible\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/typeIcon\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_margin=\"8dp\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:srcCompat=\"@drawable/ic_video_24\"\n            tools:visibility=\"visible\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <FrameLayout\n        android:id=\"@+id/selectedView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/black_a50\"\n        android:visibility=\"gone\"\n        tools:visibility=\"gone\">\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:layout_width=\"28dp\"\n            android:layout_height=\"28dp\"\n            android:layout_gravity=\"end|top\"\n            android:layout_margin=\"8dp\"\n            app:srcCompat=\"@drawable/ic_baseline_check_circle_24\"\n            app:tint=\"@color/blue_400\"\n            tools:visibility=\"visible\" />\n    </FrameLayout>\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_feed_photo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <include layout=\"@layout/item_feed_top\" />\n\n    <awais.instagrabber.customviews.drawee.ZoomableDraweeView\n        android:id=\"@+id/imageViewer\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/transparent\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        app:actualImageScaleType=\"fitCenter\"\n        app:viewAspectRatio=\"1\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_top_barrier\"/>\n\n    <include layout=\"@layout/layout_post_view_bottom\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_feed_slider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <include layout=\"@layout/item_feed_top\" />\n\n    <FrameLayout\n        android:id=\"@+id/post_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_top_barrier\">\n\n        <androidx.viewpager2.widget.ViewPager2\n            android:id=\"@+id/media_list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            tools:background=\"@color/black_a50\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_gravity=\"end|top\"\n            android:layout_margin=\"8dp\"\n            app:srcCompat=\"@drawable/ic_checkbox_multiple_blank_stroke\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/mediaCounter\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal|bottom\"\n            android:background=\"@drawable/rounder_corner_semi_black_bg\"\n            android:gravity=\"center\"\n            android:padding=\"5dp\"\n            android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n            android:textColor=\"@android:color/white\" />\n    </FrameLayout>\n\n    <include layout=\"@layout/layout_post_view_bottom\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_feed_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:parentTag=\"androidx.constraintlayout.widget.ConstraintLayout\">\n\n    <awais.instagrabber.customviews.ProfilePicView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"12dp\"\n        android:transitionName=\"profile_pic\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/title\"\n        app:layout_constraintHorizontal_bias=\"0\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:size=\"regular\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Headline6\"\n        app:layout_constrainedWidth=\"true\"\n        app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        tools:text=\"Username Username Username\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/subtitle\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/title\"\n        app:layout_constraintTop_toBottomOf=\"@id/title\"\n        tools:text=\"Full name Full name Full name Full name Full name Full name Full name \"\n        tools:visibility=\"gone\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/top_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"bottom\"/>\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/location\"\n        style=\"?borderlessButtonStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:elevation=\"0dp\"\n        android:ellipsize=\"end\"\n        android:insetTop=\"0dp\"\n        android:insetBottom=\"0dp\"\n        android:maxWidth=\"200dp\"\n        android:maxLines=\"1\"\n        android:minHeight=\"32dp\"\n        android:paddingStart=\"8dp\"\n        android:paddingEnd=\"8dp\"\n        android:textAlignment=\"viewStart\"\n        android:textAllCaps=\"false\"\n        android:textColor=\"@android:color/white\"\n        android:visibility=\"gone\"\n        app:backgroundTint=\"@color/black_a50\"\n        app:elevation=\"0dp\"\n        app:icon=\"@drawable/ic_round_location_on_24\"\n        app:iconSize=\"16dp\"\n        app:iconTint=\"@color/white\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        app:rippleColor=\"@color/grey_600\"\n        tools:text=\"Location, Location, Location, Location, \"\n        tools:visibility=\"visible\" />\n</merge>"
  },
  {
    "path": "app/src/main/res/layout/item_feed_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/videoHolder\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <include layout=\"@layout/item_feed_top\" />\n\n    <FrameLayout\n        android:id=\"@+id/post_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_top_barrier\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:layout_height=\"100dp\" />\n\n    <include layout=\"@layout/layout_post_view_bottom\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_filter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"8dp\"\n    tools:background=\"@color/black\">\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/preview\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"0dp\"\n        android:scaleType=\"centerCrop\"\n        app:layout_constraintBottom_toTopOf=\"@id/name\"\n        app:layout_constraintDimensionRatio=\"1\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/name\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:gravity=\"center\"\n        android:textColor=\"@color/filter_name_color\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/preview\"\n        tools:text=\"Filter name\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_follow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?android:selectableItemBackground\"\n    android:padding=\"16dp\">\n\n    <awais.instagrabber.customviews.ProfilePicView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"16dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/username\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:size=\"small\" />\n\n    <awais.instagrabber.customviews.UsernameTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        app:layout_constraintBottom_toTopOf=\"@id/full_name\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"username\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/full_name\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle2\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/username\"\n        app:layout_constraintTop_toBottomOf=\"@id/username\"\n        tools:text=\"Full Name\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_highlight.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"@dimen/profile_picture_size\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\"\n    android:background=\"?android:selectableItemBackground\"\n    android:orientation=\"vertical\">\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"@dimen/highlight_size\"\n        android:layout_height=\"@dimen/highlight_size\"\n        android:layout_gravity=\"center_horizontal\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:padding=\"2dp\"\n        android:scrollbars=\"none\"\n        android:singleLine=\"true\"\n        android:textStyle=\"bold\"\n        tools:text=\"@string/app_name\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_keyword.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\"\n    android:paddingBottom=\"8dp\"\n    android:paddingTop=\"8dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/keyword_text\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\" />\n\n    <Button\n        android:id=\"@+id/keyword_delete\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"\n        android:background=\"@drawable/ic_delete\"\n        android:scaleType=\"center\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_media.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:foreground=\"?android:selectableItemBackground\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/item\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:viewAspectRatio=\"1\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/duration\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginBottom=\"4dp\"\n        android:background=\"@drawable/rounder_corner_semi_black_bg\"\n        android:paddingStart=\"8dp\"\n        android:paddingEnd=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        android:textColor=\"@color/white\"\n        android:textStyle=\"bold\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        tools:text=\"1:01\"\n        tools:visibility=\"visible\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_notification.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?android:selectableItemBackground\"\n    android:baselineAligned=\"false\"\n    android:padding=\"8dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/ivProfilePic\"\n        android:layout_width=\"@dimen/notification_image_size\"\n        android:layout_height=\"@dimen/notification_image_size\"\n        android:visibility=\"visible\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintEnd_toStartOf=\"@id/barrier\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\"\n        tools:placeholderImage=\"@mipmap/ic_launcher\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierDirection=\"end\"\n        app:constraint_referenced_ids=\"ivProfilePic\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvUsername\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:paddingStart=\"16dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toTopOf=\"@id/tvComment\"\n        app:layout_constraintEnd_toStartOf=\"@id/ivPreviewPic\"\n        app:layout_constraintStart_toStartOf=\"@id/barrier\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"username\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/isVerified\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:adjustViewBounds=\"true\"\n        android:scaleType=\"fitStart\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/tvComment\"\n        app:layout_constraintEnd_toStartOf=\"@id/ivPreviewPic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/verified\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvComment\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:linksClickable=\"false\"\n        android:paddingStart=\"16dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        android:textStyle=\"italic\"\n        app:layout_constraintBottom_toTopOf=\"@id/tvSubComment\"\n        app:layout_constraintEnd_toStartOf=\"@id/ivPreviewPic\"\n        app:layout_constraintStart_toStartOf=\"@id/tvUsername\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvUsername\"\n        tools:text=\"comment comment comment comment comment comment comment comment comment comment comment comment comment \" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvSubComment\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:autoLink=\"web|email\"\n        android:ellipsize=\"end\"\n        android:linksClickable=\"false\"\n        android:paddingStart=\"16dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingRight=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        app:layout_constraintEnd_toStartOf=\"@id/preview_barrier\"\n        app:layout_constraintStart_toStartOf=\"@id/tvUsername\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvComment\"\n        tools:text=\"sub-comment long long long long long long long long long long\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/preview_barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierDirection=\"start\"\n        app:constraint_referenced_ids=\"ivPreviewPic\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/ivPreviewPic\"\n        android:layout_width=\"@dimen/notification_image_size\"\n        android:layout_height=\"@dimen/notification_image_size\"\n        android:visibility=\"gone\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/preview_barrier\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:placeholderImage=\"@mipmap/ic_launcher\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvDate\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:gravity=\"end\"\n        android:paddingStart=\"8dp\"\n        android:paddingLeft=\"8dp\"\n        android:paddingEnd=\"4dp\"\n        android:paddingRight=\"16dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n        android:textStyle=\"italic\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/ivPreviewPic\"\n        app:layout_constraintStart_toEndOf=\"@id/ivProfilePic\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSubComment\"\n        tools:text=\"some long long long long long date\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_post.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:foreground=\"?android:selectableItemBackground\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/postImage\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:viewAspectRatio=\"1\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/isDownloaded\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_gravity=\"start|top\"\n        android:layout_margin=\"8dp\"\n        android:visibility=\"gone\"\n        app:srcCompat=\"@drawable/ic_cloud_download_24\"\n        app:tint=\"@color/green_400\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/typeIcon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_gravity=\"end|top\"\n        android:layout_margin=\"8dp\"\n        app:srcCompat=\"@drawable/ic_video_24\"\n        tools:visibility=\"visible\" />\n\n    <!--<ProgressBar-->\n    <!--    android:id=\"@+id/progressView\"-->\n    <!--    android:layout_width=\"45dp\"-->\n    <!--    android:layout_height=\"45dp\"-->\n    <!--    android:layout_gravity=\"center\"-->\n    <!--    android:visibility=\"gone\" />-->\n\n    <FrameLayout\n        android:id=\"@+id/selectedView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"#8A000000\"\n        android:visibility=\"gone\"\n        tools:visibility=\"visible\">\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:layout_width=\"28dp\"\n            android:layout_height=\"28dp\"\n            android:layout_gravity=\"end|top\"\n            android:layout_margin=\"8dp\"\n            app:srcCompat=\"@drawable/ic_check_24\"\n            app:tint=\"@color/blue_300\"\n            tools:visibility=\"visible\" />\n    </FrameLayout>\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_pref_divider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/horizontal_divider_height\"\n    android:background=\"@drawable/pref_list_divider_material\" />"
  },
  {
    "path": "app/src/main/res/layout/item_search_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:paddingStart=\"16dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingEnd=\"16dp\"\n    android:paddingBottom=\"8dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n        app:layout_constraintEnd_toStartOf=\"@id/verified\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"username\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/subtitle\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"center_vertical\"\n        android:maxLines=\"1\"\n        android:paddingStart=\"16dp\"\n        android:paddingEnd=\"0dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body2\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/delete\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toBottomOf=\"@id/title\"\n        tools:text=\"full name\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/verified\"\n        android:layout_width=\"20dp\"\n        android:layout_height=\"20dp\"\n        android:layout_gravity=\"end\"\n        android:adjustViewBounds=\"true\"\n        android:scaleType=\"fitCenter\"\n        android:visibility=\"gone\"\n        app:layout_constraintBaseline_toBaselineOf=\"@id/title\"\n        app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n        app:layout_constraintStart_toEndOf=\"@id/title\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/verified\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/delete\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:padding=\"8dp\"\n        android:src=\"@drawable/ic_close_24\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:visibility=\"visible\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_slider_photo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<awais.instagrabber.customviews.drawee.DraggableZoomableDraweeView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@null\"\n    app:actualImageScaleType=\"fitCenter\"\n    tools:background=\"@mipmap/ic_launcher\" />"
  },
  {
    "path": "app/src/main/res/layout/item_story.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"@dimen/story_item_width\"\n    android:layout_height=\"@dimen/story_item_height\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:layout_margin=\"4dp\"\n    android:foreground=\"?android:selectableItemBackground\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:actualImageScaleType=\"fitCenter\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/selectedView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:drawable/gallery_thumb\"\n        android:visibility=\"gone\" />\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_tab_order_pref.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"56dp\"\n    android:orientation=\"horizontal\">\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/add_remove\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:scaleType=\"centerInside\"\n        tools:srcCompat=\"@drawable/ic_round_add_circle_24\"\n        tools:tint=\"@color/green_500\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:scaleType=\"centerInside\"\n        tools:srcCompat=\"@drawable/ic_home_24\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        tools:text=\"@string/feed\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/handle\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginEnd=\"16dp\"\n        android:scaleType=\"centerInside\"\n        app:srcCompat=\"@drawable/ic_round_drag_handle_24\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_controls.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/controller\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:gravity=\"center\"\n    android:orientation=\"horizontal\">\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@id/exo_pause\"\n        android:layout_width=\"71dp\"\n        android:layout_height=\"64dp\"\n        android:background=\"?android:selectableItemBackground\"\n        android:contentDescription=\"@string/exo_controls_pause_description\"\n        android:src=\"@drawable/exo_icon_pause\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@id/exo_play\"\n        android:layout_width=\"71dp\"\n        android:layout_height=\"64dp\"\n        android:background=\"?android:selectableItemBackground\"\n        android:contentDescription=\"@string/exo_controls_play_description\"\n        android:src=\"@drawable/exo_icon_play\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_direct_item_options.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.cardview.widget.CardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/card\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    app:cardCornerRadius=\"8dp\"\n    app:cardElevation=\"2dp\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\">\n\n        <!--<androidx.appcompat.widget.AppCompatImageView-->\n        <!--    android:id=\"@+id/img1\"-->\n        <!--    android:layout_width=\"48dp\"-->\n        <!--    android:layout_height=\"0dp\"-->\n        <!--    android:padding=\"4dp\"-->\n        <!--    android:scaleType=\"fitCenter\"-->\n        <!--    app:layout_constraintBottom_toTopOf=\"@id/divider\"-->\n        <!--    app:layout_constraintDimensionRatio=\"1\"-->\n        <!--    app:layout_constraintEnd_toStartOf=\"@id/img2\"-->\n        <!--    app:layout_constraintStart_toStartOf=\"parent\"-->\n        <!--    app:layout_constraintTop_toTopOf=\"parent\"-->\n        <!--    tools:srcCompat=\"@mipmap/ic_launcher\" />-->\n\n        <!--<androidx.appcompat.widget.AppCompatImageView-->\n        <!--    android:id=\"@+id/img2\"-->\n        <!--    android:layout_width=\"48dp\"-->\n        <!--    android:layout_height=\"0dp\"-->\n        <!--    android:padding=\"4dp\"-->\n        <!--    android:scaleType=\"fitCenter\"-->\n        <!--    app:layout_constraintDimensionRatio=\"1\"-->\n        <!--    app:layout_constraintEnd_toStartOf=\"@id/img3\"-->\n        <!--    app:layout_constraintStart_toEndOf=\"@id/img1\"-->\n        <!--    app:layout_constraintTop_toTopOf=\"parent\"-->\n        <!--    tools:srcCompat=\"@mipmap/ic_launcher\" />-->\n\n        <!--<awais.instagrabber.customviews.SquareImageView-->\n        <!--    android:id=\"@+id/img3\"-->\n        <!--    android:layout_width=\"48dp\"-->\n        <!--    android:layout_height=\"48dp\"-->\n        <!--    android:padding=\"4dp\"-->\n        <!--    app:layout_constraintEnd_toEndOf=\"parent\"-->\n        <!--    app:layout_constraintStart_toEndOf=\"@id/img2\"-->\n        <!--    app:layout_constraintTop_toTopOf=\"parent\"-->\n        <!--    tools:srcCompat=\"@mipmap/ic_launcher\" />-->\n\n        <!--<include-->\n        <!--    android:id=\"@+id/divider\"-->\n        <!--    layout=\"@layout/item_pref_divider\"-->\n        <!--    android:layout_width=\"0dp\"-->\n        <!--    android:layout_height=\"1dp\"-->\n        <!--    app:layout_constraintEnd_toEndOf=\"@id/img3\"-->\n        <!--    app:layout_constraintStart_toStartOf=\"@id/img1\"-->\n        <!--    app:layout_constraintTop_toBottomOf=\"@id/img1\" />-->\n\n        <!--<androidx.appcompat.widget.AppCompatTextView-->\n        <!--    android:layout_width=\"0dp\"-->\n        <!--    android:layout_height=\"wrap_content\"-->\n        <!--    android:text=\"test\"-->\n        <!--    app:layout_constraintBottom_toBottomOf=\"parent\"-->\n        <!--    app:layout_constraintEnd_toEndOf=\"parent\"-->\n        <!--    app:layout_constraintStart_toStartOf=\"parent\"-->\n        <!--    app:layout_constraintTop_toBottomOf=\"@id/divider\" />-->\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</androidx.cardview.widget.CardView>"
  },
  {
    "path": "app/src/main/res/layout/layout_directory_chooser.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/appBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toTopOf=\"@id/directoryList\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.google.android.material.appbar.MaterialToolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:navigationIcon=\"@drawable/ic_arrow_upward_24\"\n            tools:title=\"/this/that/thy\" />\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/directoryList\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/bottom_horizontal_divider\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/appBarLayout\" />\n\n    <include\n        android:id=\"@+id/bottom_horizontal_divider\"\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/btnCancel\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/directoryList\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnCancel\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/cancel\"\n        app:icon=\"@drawable/ic_close_24\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/directoryList\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/btnConfirm\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/confirm\"\n        app:icon=\"@drawable/ic_check_24\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/directoryList\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_action_log.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.AppCompatTextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/tvMessage\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:paddingStart=\"@dimen/dm_message_card_radius\"\n    android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n    android:paddingEnd=\"@dimen/dm_message_card_radius\"\n    android:paddingBottom=\"@dimen/dm_message_card_radius_small\"\n    android:textAlignment=\"center\"\n    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n    tools:text=\"Text message\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_animated_media.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.facebook.drawee.view.SimpleDraweeView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/ivAnimatedMessage\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/dm_media_img_max_height\"\n    app:actualImageScaleType=\"fitXY\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_base.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<awais.instagrabber.customviews.DirectItemFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"start\"\n        android:clipToPadding=\"false\"\n        android:paddingStart=\"4dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"4dp\"\n        tools:layout_gravity=\"end\"\n        tools:paddingBottom=\"@dimen/dm_reaction_adjust_margin\">\n\n        <View\n            android:id=\"@+id/quote_line\"\n            android:layout_width=\"4dp\"\n            android:layout_height=\"0dp\"\n            android:background=\"@drawable/bg_quote_line\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"@id/reply_container\"\n            app:layout_constraintEnd_toStartOf=\"@id/reply_container\"\n            app:layout_constraintHorizontal_bias=\"0\"\n            app:layout_constraintHorizontal_chainStyle=\"packed\"\n            app:layout_constraintStart_toEndOf=\"@id/ivProfilePic\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            tools:visibility=\"gone\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/reply_info\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"4dp\"\n            android:layout_marginEnd=\"4dp\"\n            android:padding=\"4dp\"\n            android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n            app:layout_constraintBottom_toTopOf=\"@id/reply_container\"\n            app:layout_constraintStart_toEndOf=\"@id/quote_line\"\n            app:layout_constraintTop_toTopOf=\"parent\"\n            app:layout_goneMarginStart=\"0dp\"\n            tools:text=\"Replied to you\" />\n\n        <FrameLayout\n            android:id=\"@+id/reply_container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"4dp\"\n            android:layout_marginEnd=\"4dp\"\n            android:layout_marginBottom=\"4dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/tvUsername\"\n\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"1\"\n            app:layout_constraintHorizontal_chainStyle=\"packed\"\n            app:layout_constraintStart_toEndOf=\"@id/quote_line\"\n            app:layout_constraintTop_toBottomOf=\"@id/reply_info\">\n\n            <com.facebook.drawee.view.SimpleDraweeView\n                android:id=\"@+id/reply_image\"\n                android:layout_width=\"100dp\"\n                android:layout_height=\"100dp\"\n                android:visibility=\"gone\"\n                app:actualImageScaleType=\"centerCrop\"\n                app:roundedCornerRadius=\"@dimen/dm_message_card_radius\"\n                tools:visibility=\"gone\" />\n\n            <androidx.appcompat.widget.AppCompatTextView\n                android:id=\"@+id/reply_text\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/bg_reply_text\"\n                android:padding=\"@dimen/dm_message_card_radius_small\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"14sp\"\n                android:visibility=\"gone\"\n                tools:text=\"Some message\"\n                tools:visibility=\"gone\" />\n        </FrameLayout>\n\n        <awais.instagrabber.customviews.CircularImageView\n            android:id=\"@+id/ivProfilePic\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:layout_marginEnd=\"4dp\"\n            android:visibility=\"gone\"\n            app:layout_constraintEnd_toStartOf=\"@id/chat_message_layout\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/tvUsername\"\n            tools:visibility=\"gone\" />\n\n        <View\n            android:id=\"@+id/background\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"0dp\"\n            app:layout_constraintBottom_toBottomOf=\"@id/chat_message_layout\"\n            app:layout_constraintEnd_toEndOf=\"@id/chat_message_layout\"\n            app:layout_constraintStart_toStartOf=\"@id/chat_message_layout\"\n            app:layout_constraintTop_toTopOf=\"@id/tvUsername\"\n            tools:background=\"@drawable/bg_speech_bubble_incoming\" />\n\n        <androidx.appcompat.widget.AppCompatTextView\n            android:id=\"@+id/tvUsername\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:paddingStart=\"@dimen/dm_message_card_radius\"\n            android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n            android:paddingEnd=\"@dimen/dm_message_card_radius\"\n            android:paddingBottom=\"4dp\"\n            android:singleLine=\"true\"\n            app:layout_constraintBottom_toTopOf=\"@id/chat_message_layout\"\n            app:layout_constraintEnd_toEndOf=\"@id/chat_message_layout\"\n            app:layout_constraintHorizontal_bias=\"0\"\n            app:layout_constraintStart_toStartOf=\"@id/chat_message_layout\"\n            app:layout_constraintTop_toBottomOf=\"@id/reply_container\"\n            tools:text=\"@string/app_name\"\n            tools:visibility=\"visible\" />\n\n        <!--app:layout_constraintBottom_toTopOf=\"@id/reactions\"-->\n        <awais.instagrabber.customviews.ChatMessageLayout\n            android:id=\"@+id/chat_message_layout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@id/ivProfilePic\"\n            app:layout_constraintTop_toBottomOf=\"@id/tvUsername\"\n            app:layout_constraintWidth_min=\"wrap\"\n            app:viewPartInfo=\"@id/message_info\"\n            app:viewPartMain=\"@id/message\">\n\n            <FrameLayout\n                android:id=\"@+id/message\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                tools:layout_height=\"200dp\"\n                tools:layout_width=\"100dp\">\n                <!--<include layout=\"@layout/layout_dm_text\" />-->\n            </FrameLayout>\n\n            <LinearLayout\n                android:id=\"@+id/message_info\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"end|bottom\"\n                android:gravity=\"end|bottom\"\n                android:minWidth=\"60dp\"\n                android:orientation=\"horizontal\"\n                android:paddingBottom=\"@dimen/dm_message_card_radius_small\">\n\n                <TextView\n                    android:id=\"@+id/message_time\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center_vertical\"\n                    android:gravity=\"center_vertical\"\n                    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n                    android:textColor=\"@color/grey_500\"\n                    tools:text=\"12:00 am\" />\n\n                <androidx.appcompat.widget.AppCompatImageView\n                    android:id=\"@+id/delivery_status\"\n                    android:layout_width=\"16dp\"\n                    android:layout_height=\"16dp\"\n                    android:layout_gravity=\"bottom\"\n                    android:layout_marginStart=\"4dp\"\n                    android:gravity=\"center_vertical\"\n                    android:visibility=\"gone\"\n                    app:srcCompat=\"@drawable/ic_check_all_24\"\n                    app:tint=\"@color/grey_500\"\n                    tools:visibility=\"visible\" />\n            </LinearLayout>\n\n        </awais.instagrabber.customviews.ChatMessageLayout>\n\n        <!--<TextView-->\n        <!--    android:tag=\"debug\"-->\n        <!--    android:textColor=\"@color/white\"-->\n        <!--    android:layout_width=\"0dp\"-->\n        <!--    android:visibility=\"visible\"-->\n        <!--    android:layout_height=\"wrap_content\"-->\n        <!--    android:background=\"@color/black\"-->\n        <!--    app:layout_constraintTop_toTopOf=\"parent\"-->\n        <!--    app:layout_constraintStart_toStartOf=\"parent\"-->\n        <!--    app:layout_constraintEnd_toEndOf=\"parent\"/>-->\n\n        <FrameLayout\n            android:id=\"@+id/reactions_wrapper\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"4dp\"\n            android:layout_marginBottom=\"4dp\"\n            android:paddingBottom=\"2dp\"\n            android:translationY=\"@dimen/dm_reaction_translation_y_type_1\"\n            android:visibility=\"gone\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"@id/chat_message_layout\"\n            app:layout_constraintTop_toBottomOf=\"parent\"\n            tools:visibility=\"visible\">\n\n            <awais.instagrabber.customviews.ReactionEmojiTextView\n                android:id=\"@+id/emojis\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/bg_rounded_corner\"\n                android:elevation=\"1dp\"\n                android:ellipsize=\"end\"\n                android:padding=\"4dp\"\n                android:singleLine=\"true\"\n                android:textColor=\"?android:textColorPrimary\"\n                android:textSize=\"18sp\"\n                tools:text=\"😀😀😀😀😀😀😀\" />\n        </FrameLayout>\n\n        <!--<FrameLayout-->\n        <!--    android:id=\"@+id/reactions\"-->\n        <!--    android:layout_width=\"0dp\"-->\n        <!--    android:layout_height=\"wrap_content\"-->\n        <!--    android:background=\"@drawable/bg_rounded_corner\"-->\n        <!--    android:elevation=\"1dp\"-->\n        <!--    android:visibility=\"gone\"-->\n        <!--    app:layout_constraintEnd_toEndOf=\"@id/chat_message_layout\"-->\n        <!--    app:layout_constraintStart_toStartOf=\"@id/chat_message_layout\"-->\n        <!--    app:layout_constraintTop_toBottomOf=\"@id/chat_message_layout\"-->\n        <!--    tools:visibility=\"visible\">-->\n\n        <!--    <androidx.appcompat.widget.AppCompatTextView-->\n        <!--        android:id=\"@+id/emojis\"-->\n        <!--        android:layout_width=\"wrap_content\"-->\n        <!--        android:layout_height=\"wrap_content\"-->\n        <!--        android:padding=\"4dp\"-->\n        <!--        android:textColor=\"?android:textColorPrimary\"-->\n        <!--        tools:text=\"😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀\" />-->\n        <!--</FrameLayout>-->\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</awais.instagrabber.customviews.DirectItemFrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/header\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginTop=\"4dp\"\n        android:layout_marginBottom=\"4dp\"\n        android:background=\"@drawable/bg_dm_date_header\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        android:textColor=\"@color/white\"\n        tools:text=\"24-11-2020\" />\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_inbox_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"72dp\"\n    android:background=\"?android:selectableItemBackground\"\n    android:orientation=\"horizontal\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"16dp\">\n\n    <FrameLayout\n        android:id=\"@+id/profile_pic_container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/profile_pic\"\n            android:layout_width=\"@dimen/dm_inbox_avatar_size\"\n            android:layout_height=\"@dimen/dm_inbox_avatar_size\"\n            app:actualImageScaleType=\"centerCrop\"\n            app:roundAsCircle=\"true\"\n            tools:background=\"@mipmap/ic_launcher\"\n            tools:visibility=\"gone\" />\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:id=\"@+id/multi_pic_container\"\n            android:layout_width=\"@dimen/dm_inbox_avatar_size\"\n            android:layout_height=\"@dimen/dm_inbox_avatar_size\"\n            android:visibility=\"gone\"\n            tools:visibility=\"visible\">\n\n            <com.facebook.drawee.view.SimpleDraweeView\n                android:id=\"@+id/multi_pic_1\"\n                android:layout_width=\"@dimen/dm_inbox_avatar_size_tiny\"\n                android:layout_height=\"@dimen/dm_inbox_avatar_size_tiny\"\n                app:layout_constraintStart_toStartOf=\"parent\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                app:roundAsCircle=\"true\"\n                tools:background=\"@mipmap/ic_launcher\" />\n\n            <com.facebook.drawee.view.SimpleDraweeView\n                android:id=\"@+id/multi_pic_2\"\n                android:layout_width=\"@dimen/dm_inbox_avatar_size_tiny\"\n                android:layout_height=\"@dimen/dm_inbox_avatar_size_tiny\"\n                app:layout_constraintBottom_toBottomOf=\"parent\"\n                app:layout_constraintEnd_toEndOf=\"parent\"\n                app:layout_constraintStart_toStartOf=\"parent\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                app:roundAsCircle=\"true\"\n                tools:background=\"@mipmap/ic_launcher\" />\n\n            <com.facebook.drawee.view.SimpleDraweeView\n                android:id=\"@+id/multi_pic_3\"\n                android:layout_width=\"@dimen/dm_inbox_avatar_size_tiny\"\n                android:layout_height=\"@dimen/dm_inbox_avatar_size_tiny\"\n                app:layout_constraintBottom_toBottomOf=\"parent\"\n                app:layout_constraintEnd_toEndOf=\"parent\"\n                app:roundAsCircle=\"true\"\n                tools:background=\"@mipmap/ic_launcher\" />\n        </androidx.constraintlayout.widget.ConstraintLayout>\n    </FrameLayout>\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/thread_title\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"bottom\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n        app:layout_constraintEnd_toStartOf=\"@id/tvDate\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic_container\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        tools:text=\"username, username, username\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/tvDate\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"bottom\"\n        android:paddingStart=\"4dp\"\n        android:paddingEnd=\"0dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        app:layout_constraintBaseline_toBaselineOf=\"@id/thread_title\"\n        app:layout_constraintBottom_toTopOf=\"@id/subtitle\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/thread_title\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"long date...............\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/subtitle\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/unread\"\n        app:layout_constraintStart_toStartOf=\"@id/thread_title\"\n        app:layout_constraintTop_toBottomOf=\"@id/thread_title\"\n        tools:text=\"comment comment comment comment comment comment comment comment comment comment comment comment comment \" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/unread\"\n        android:layout_width=\"8dp\"\n        android:layout_height=\"0dp\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginEnd=\"4dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/subtitle\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/subtitle\"\n        app:layout_constraintTop_toTopOf=\"@id/subtitle\"\n        app:srcCompat=\"@drawable/ic_unread_indicator_24\"\n        tools:visibility=\"visible\" />\n\n    <include\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/thread_title\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_like.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.AppCompatImageButton xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/tvMessage\"\n    android:layout_width=\"68dp\"\n    android:layout_height=\"68dp\"\n    android:background=\"@android:color/transparent\"\n    android:scaleType=\"centerCrop\"\n    android:src=\"@drawable/ic_like\"\n    android:tint=\"@color/red_800\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_link.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/link_container\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"@dimen/dm_message_card_radius\"\n        android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n        android:paddingEnd=\"@dimen/dm_message_card_radius\"\n        android:paddingBottom=\"@dimen/dm_message_card_radius_small\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        tools:text=\"Some message\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/dm_link_image_size\"\n        android:scaleType=\"centerCrop\"\n        tools:background=\"@mipmap/ic_launcher\"\n        tools:layout_width=\"300dp\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"3\"\n        android:paddingStart=\"@dimen/dm_message_card_radius\"\n        android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n        android:paddingEnd=\"@dimen/dm_message_card_radius\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"@color/white\"\n        tools:text=\"Title of the website\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/summary\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"3\"\n        android:paddingStart=\"@dimen/dm_message_card_radius\"\n        android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n        android:paddingEnd=\"@dimen/dm_message_card_radius\"\n        android:textColor=\"@color/white\"\n        tools:text=\"Some summary of the website\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/url\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:paddingStart=\"@dimen/dm_message_card_radius\"\n        android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n        android:paddingEnd=\"@dimen/dm_message_card_radius\"\n        android:paddingBottom=\"@dimen/dm_message_card_radius_small\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        android:textColor=\"@color/white\"\n        tools:text=\"test.com\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_media.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/media_container\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/media_preview\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/typeIcon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_gravity=\"end|top\"\n        android:layout_margin=\"8dp\"\n        app:srcCompat=\"@drawable/ic_video_24\" />\n\n    <View\n        android:id=\"@+id/bg_time\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"50dp\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"@drawable/bg_dm_time\" />\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_media_share.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/media_share_container\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/top_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"bottom\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/media_preview\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:layout_height=\"200dp\" />\n\n    <View\n        android:id=\"@+id/top_bg\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/bg_media_share_top_incoming\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"@dimen/profile_pic_size_regular\"\n        android:layout_height=\"@dimen/profile_pic_size_regular\"\n        android:layout_margin=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/username\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toStartOf=\"@id/type_icon\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        tools:text=\"Username Username Username Username Username Username \" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/type_icon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_margin=\"8dp\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/username\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        app:srcCompat=\"@drawable/ic_video_24\" />\n\n    <View\n        android:id=\"@+id/bottom_bg\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/bg_media_share_bottom\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/caption\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/caption\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        tools:text=\"Caption Caption Caption Caption Caption Caption \" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_pending_user_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"72dp\"\n    android:background=\"?android:selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:orientation=\"horizontal\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"16dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"@dimen/dm_inbox_avatar_size\"\n        android:layout_height=\"@dimen/dm_inbox_avatar_size\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/username\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"bottom\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        app:layout_constraintBottom_toTopOf=\"@id/requester\"\n        app:layout_constraintEnd_toStartOf=\"@id/controls_barrier\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        tools:text=\"username.......................\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/requester\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"@id/username\"\n        app:layout_constraintStart_toStartOf=\"@id/username\"\n        app:layout_constraintTop_toBottomOf=\"@id/username\"\n        tools:text=\"Added by someone\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/controls_barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"start\"\n        app:constraint_referenced_ids=\"progress,approve,deny\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/approve\"\n        android:layout_width=\"42dp\"\n        android:layout_height=\"42dp\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:scaleType=\"centerInside\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/deny\"\n        app:layout_constraintStart_toEndOf=\"@id/controls_barrier\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_check_24\"\n        app:tint=\"@color/green_500\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/deny\"\n        android:layout_width=\"42dp\"\n        android:layout_height=\"42dp\"\n        android:layout_marginStart=\"8dp\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:scaleType=\"centerInside\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/approve\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        app:srcCompat=\"@drawable/ic_close_24\"\n        app:tint=\"@color/red_500\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.progressindicator.CircularProgressIndicator\n        android:id=\"@+id/progress\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:indeterminate=\"true\"\n        android:visibility=\"gone\"\n        app:indicatorColor=\"?colorSurface\"\n        app:indicatorSize=\"30dp\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/controls_barrier\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        app:trackColor=\"@color/blue_900\"\n        tools:visibility=\"gone\" />\n\n    <include\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/thread_title\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/profile_container\"\n    android:layout_width=\"240dp\"\n    android:layout_height=\"wrap_content\">\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"@dimen/profile_pic_size_regular\"\n        android:layout_height=\"@dimen/profile_pic_size_regular\"\n        android:layout_margin=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/username\"\n        app:layout_constraintHorizontal_bias=\"0\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"bottom\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"16sp\"\n        android:textStyle=\"bold\"\n        app:layout_constrainedWidth=\"true\"\n        app:layout_constraintBottom_toTopOf=\"@id/full_name\"\n        app:layout_constraintEnd_toStartOf=\"@id/isVerified\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_goneMarginBottom=\"8dp\"\n        app:layout_goneMarginEnd=\"8dp\"\n        app:layout_goneMarginStart=\"8dp\"\n        tools:text=\"Long..long... Long..long... Long..long...\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/isVerified\"\n        android:layout_width=\"32dp\"\n        android:layout_height=\"0dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintBottom_toBottomOf=\"@id/username\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/username\"\n        app:layout_constraintTop_toTopOf=\"@id/username\"\n        app:srcCompat=\"@drawable/verified\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/full_name\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"8dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:ellipsize=\"end\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"@color/white\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/username\"\n        app:layout_constraintTop_toBottomOf=\"@id/username\"\n        tools:text=\"Full name\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"bottom\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_1\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toTopOf=\"@id/space_3\"\n        app:layout_constraintEnd_toStartOf=\"@id/space_1\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/barrier\"\n        app:viewAspectRatio=\"1\" />\n\n    <Space\n        android:id=\"@+id/space_1\"\n        android:layout_width=\"1dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/preview_2\"\n        app:layout_constraintStart_toEndOf=\"@id/preview_1\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_1\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_2\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"@id/preview_1\"\n        app:layout_constraintEnd_toStartOf=\"@id/space_2\"\n        app:layout_constraintStart_toEndOf=\"@id/space_1\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_1\"\n        app:viewAspectRatio=\"1\" />\n\n    <Space\n        android:id=\"@+id/space_2\"\n        android:layout_width=\"1dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/preview_3\"\n        app:layout_constraintStart_toEndOf=\"@id/preview_2\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_1\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_3\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"@id/preview_1\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/space_2\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_1\"\n        app:viewAspectRatio=\"1\" />\n\n    <Space\n        android:id=\"@+id/space_3\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/preview_4\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/preview_1\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_4\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/space_1\"\n        app:layout_constraintStart_toStartOf=\"@id/preview_1\"\n        app:layout_constraintTop_toBottomOf=\"@id/space_3\"\n        app:viewAspectRatio=\"1\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_5\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/space_2\"\n        app:layout_constraintStart_toEndOf=\"@id/space_1\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_4\"\n        app:viewAspectRatio=\"1\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview_6\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/space_2\"\n        app:layout_constraintTop_toTopOf=\"@id/preview_4\"\n        app:viewAspectRatio=\"1\" />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/first_row\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:constraint_referenced_ids=\"preview_1,preview_2,preview_3\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Group\n        android:id=\"@+id/second_row\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:constraint_referenced_ids=\"space_3, preview_4,preview_5,preview_6\"\n        tools:visibility=\"gone\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_raven_media.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/raven_media_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/expiry_info\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:drawablePadding=\"8dp\"\n        android:gravity=\"start|center_vertical\"\n        android:padding=\"8dp\"\n        app:drawableStartCompat=\"@drawable/ic_clock_alert_outline_24\"\n        tools:text=\"Image will expire\" />\n\n    <FrameLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/preview\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            app:actualImageScaleType=\"centerCrop\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/typeIcon\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_gravity=\"end|top\"\n            android:layout_margin=\"8dp\"\n            app:srcCompat=\"@drawable/ic_video_24\" />\n    </FrameLayout>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_reel_share.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/reel_share_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/share_info\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/quote_line\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_bias=\"0\"\n        tools:gravity=\"end\"\n        tools:text=\"Replied to story\" />\n\n    <View\n        android:id=\"@+id/quote_line\"\n        android:layout_width=\"4dp\"\n        android:layout_height=\"0dp\"\n        android:layout_marginStart=\"4dp\"\n        android:background=\"@drawable/bg_quote_line\"\n        app:layout_constraintBottom_toBottomOf=\"@id/preview\"\n        app:layout_constraintEnd_toStartOf=\"@id/preview\"\n        app:layout_constraintHorizontal_bias=\"0\"\n        app:layout_constraintHorizontal_chainStyle=\"packed\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/share_info\"\n        tools:layout_constraintHorizontal_bias=\"1\"\n        tools:visibility=\"gone\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/preview\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"150dp\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginBottom=\"4dp\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toTopOf=\"@id/message\"\n        app:layout_constraintEnd_toStartOf=\"@id/quote_line_end\"\n        app:layout_constraintStart_toEndOf=\"@id/quote_line\"\n        app:layout_constraintTop_toBottomOf=\"@id/share_info\"\n        tools:background=\"@mipmap/ic_launcher\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/type_icon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_margin=\"8dp\"\n        app:layout_constraintEnd_toEndOf=\"@id/preview\"\n        app:layout_constraintTop_toTopOf=\"@id/preview\"\n        app:srcCompat=\"@drawable/ic_video_24\"\n        tools:visibility=\"visible\" />\n\n    <View\n        android:id=\"@+id/quote_line_end\"\n        android:layout_width=\"4dp\"\n        android:layout_height=\"0dp\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginEnd=\"4dp\"\n        android:background=\"@drawable/bg_quote_line\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/preview\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/preview\"\n        app:layout_constraintTop_toBottomOf=\"@id/share_info\"\n        tools:visibility=\"visible\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/message\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"@dimen/dm_message_card_radius\"\n        android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n        android:paddingEnd=\"@dimen/dm_message_card_radius\"\n        android:paddingBottom=\"@dimen/dm_message_card_radius_small\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/preview\"\n        tools:text=\"Some message\" />\n\n    <!-- Need to make textColor black, so that emojis are rendered opaque -->\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/reaction\"\n        android:layout_width=\"42dp\"\n        android:layout_height=\"42dp\"\n        android:textColor=\"@color/black\"\n        app:autoSizeTextType=\"uniform\"\n        app:layout_constraintBottom_toBottomOf=\"@id/preview\"\n        app:layout_constraintEnd_toEndOf=\"@id/preview\"\n        app:layout_constraintStart_toEndOf=\"@id/preview\"\n        app:layout_constraintTop_toTopOf=\"@id/preview\"\n        tools:text=\"😮\"\n        tools:visibility=\"gone\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_story_share.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/story_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/share_info\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        tools:text=\"You shared someones story\" />\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/ivMediaPreview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:maxHeight=\"@dimen/dm_media_img_max_height\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:id=\"@+id/typeIcon\"\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_gravity=\"end|top\"\n            android:layout_margin=\"8dp\"\n            android:visibility=\"gone\"\n            app:srcCompat=\"@drawable/ic_video_24\" />\n    </FrameLayout>\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"start|fill_horizontal\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        tools:text=\"Some text \" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<awais.instagrabber.customviews.RamboTextViewV2 xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/tvMessage\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:paddingStart=\"@dimen/dm_message_card_radius\"\n    android:paddingTop=\"@dimen/dm_message_card_radius_small\"\n    android:paddingEnd=\"@dimen/dm_message_card_radius\"\n    android:paddingBottom=\"@dimen/dm_message_card_radius_small\"\n    android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n    android:textColor=\"@color/white\"\n    tools:text=\"So here you can see the timestamp overlaps lol tt tt tt tt tt tt tt tt tt tt t\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_user_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"72dp\"\n    android:background=\"?android:selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:orientation=\"horizontal\"\n    android:paddingStart=\"16dp\"\n    android:paddingEnd=\"16dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"@dimen/dm_inbox_avatar_size\"\n        android:layout_height=\"@dimen/dm_inbox_avatar_size\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/full_name\"\n        app:layout_constraintHorizontal_chainStyle=\"spread_inside\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/profile_pic2\"\n        android:layout_width=\"@dimen/dm_inbox_avatar_size\"\n        android:layout_height=\"@dimen/dm_inbox_avatar_size\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/full_name\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"bottom\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Subtitle1\"\n        app:layout_constraintBottom_toTopOf=\"@id/username\"\n        app:layout_constraintEnd_toStartOf=\"@id/info\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        tools:text=\"Long name.......................\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/info\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"bottom\"\n        android:paddingStart=\"4dp\"\n        android:paddingEnd=\"0dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        android:textColor=\"@color/blue_400\"\n        app:layout_constraintBaseline_toBaselineOf=\"@id/thread_title\"\n        app:layout_constraintBottom_toTopOf=\"@id/username\"\n        app:layout_constraintEnd_toStartOf=\"@id/select\"\n        app:layout_constraintStart_toEndOf=\"@id/full_name\"\n        app:layout_constraintTop_toTopOf=\"@id/full_name\"\n        tools:text=\"Admin\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/select\"\n        app:layout_constraintStart_toStartOf=\"@id/full_name\"\n        app:layout_constraintTop_toBottomOf=\"@id/full_name\"\n        tools:text=\"username\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/select\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"\n        android:layout_marginStart=\"4dp\"\n        android:duplicateParentState=\"true\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toStartOf=\"@id/secondary_image\"\n        app:layout_constraintStart_toEndOf=\"@id/info\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        app:srcCompat=\"@drawable/ic_circle_check\"\n        app:tint=\"@color/ic_circle_check_tint\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/secondary_image\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"\n        android:layout_marginStart=\"4dp\"\n        android:duplicateParentState=\"true\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/profile_pic\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/select\"\n        app:layout_constraintTop_toTopOf=\"@id/profile_pic\"\n        tools:srcCompat=\"@mipmap/ic_launcher\"\n        tools:visibility=\"gone\" />\n\n    <include\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"1dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/thread_title\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_dm_voice_media.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/voice_media\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <FrameLayout\n        android:id=\"@+id/play_wrapper\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginStart=\"4dp\"\n        app:layout_constraintBottom_toBottomOf=\"@id/waveformSeekBar\"\n        app:layout_constraintEnd_toStartOf=\"@id/waveformSeekBar\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.google.android.material.button.MaterialButton\n            android:id=\"@+id/play_pause\"\n            style=\"@style/Widget.MaterialComponents.Button.Icon\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:insetLeft=\"1px\"\n            android:insetTop=\"0dp\"\n            android:insetRight=\"0dp\"\n            android:insetBottom=\"0dp\"\n            android:paddingStart=\"0dp\"\n            android:paddingEnd=\"0dp\"\n            android:visibility=\"visible\"\n            app:backgroundTint=\"@color/blue_900\"\n            app:elevation=\"8dp\"\n            app:icon=\"@drawable/ic_round_play_arrow_24\"\n            app:iconGravity=\"textStart\"\n            app:iconPadding=\"0dp\"\n            app:iconSize=\"24dp\"\n            app:iconTint=\"@color/white\"\n            app:shapeAppearanceOverlay=\"@style/ShapeAppearanceOverlay.App.Button.Circle\"\n            tools:visibility=\"visible\" />\n\n        <com.google.android.material.progressindicator.CircularProgressIndicator\n            android:id=\"@+id/progress_bar\"\n            android:layout_width=\"40dp\"\n            android:layout_height=\"40dp\"\n            android:layout_gravity=\"center\"\n            android:indeterminate=\"true\"\n            android:visibility=\"gone\"\n            app:indicatorColor=\"?colorSurface\"\n            app:trackColor=\"@color/blue_900\"\n            tools:visibility=\"gone\" />\n    </FrameLayout>\n\n    <!--<com.frolo.waveformseekbar.WaveformSeekBar-->\n    <!--    android:id=\"@+id/waveformSeekBar\"-->\n    <!--    android:layout_width=\"0dp\"-->\n    <!--    android:layout_height=\"54dp\"-->\n    <!--    android:layout_marginStart=\"8dp\"-->\n    <!--    android:layout_marginEnd=\"8dp\"-->\n    <!--    android:layout_weight=\"1\"-->\n    <!--    app:waveBackgroundColor=\"@color/white\"-->\n    <!--    app:waveProgressColor=\"@color/blue_400\" />-->\n\n    <awais.instagrabber.customviews.masoudss_waveform.WaveformSeekBar\n        android:id=\"@+id/waveformSeekBar\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"54dp\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"4dp\"\n        app:waveformBackgroundColor=\"?dmWaveformBgColor\"\n        app:layout_constraintBottom_toTopOf=\"@id/duration\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/play_wrapper\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:waveformProgressColor=\"?dmWaveformProgressColor\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/duration\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Caption\"\n        android:textColor=\"?android:textColorPrimary\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"@id/waveformSeekBar\"\n        tools:text=\"00:00/00:00\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_emoji_variant_popup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\">\n\n    <androidx.cardview.widget.CardView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:cardCornerRadius=\"1dp\"\n        app:cardElevation=\"2dp\"\n        app:cardUseCompatPadding=\"true\">\n\n        <LinearLayout\n            android:id=\"@+id/container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"4dp\"\n            android:orientation=\"horizontal\" />\n    </androidx.cardview.widget.CardView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/layout_exo_custom_controls.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <View\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@color/exo_black_opacity_60\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/top_barrier\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/top_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"top\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/exo_rew_with_amount\"\n        style=\"@style/Widget.App.MaterialButton.IconOnly.BorderlessRipple\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_replay_5_24_states\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/white\"\n        app:layout_constraintBottom_toTopOf=\"@id/progress_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/exo_play_pause\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:enabled=\"false\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/exo_play_pause\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:contentDescription=\"@string/exo_controls_play_description\"\n        android:padding=\"8dp\"\n        android:scaleType=\"fitCenter\"\n        android:src=\"@drawable/exo_styled_controls_play\"\n        app:layout_constraintBottom_toTopOf=\"@id/progress_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/exo_ffwd_with_amount\"\n        app:layout_constraintStart_toEndOf=\"@id/exo_rew_with_amount\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:enabled=\"false\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/exo_ffwd_with_amount\"\n        style=\"@style/Widget.App.MaterialButton.IconOnly.BorderlessRipple\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:icon=\"@drawable/ic_forward_5_24_states\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/white\"\n        app:layout_constraintBottom_toTopOf=\"@id/progress_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/mute\"\n        app:layout_constraintStart_toEndOf=\"@id/exo_play_pause\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:enabled=\"false\"\n        tools:visibility=\"gone\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/mute\"\n        style=\"@style/Widget.App.MaterialButton.IconOnly.BorderlessRipple\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:icon=\"@drawable/ic_volume_off_24_states\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/white\"\n        app:layout_constraintBottom_toTopOf=\"@id/progress_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/exo_settings\"\n        app:layout_constraintStart_toEndOf=\"@id/exo_ffwd_with_amount\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:enabled=\"false\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/exo_settings\"\n        style=\"@style/Widget.App.MaterialButton.IconOnly.BorderlessRipple\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:icon=\"@drawable/exo_ic_settings\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/white\"\n        app:layout_constraintBottom_toTopOf=\"@id/progress_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/mute\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:enabled=\"false\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/progress_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"top\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/exo_position\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/exo_progress\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/progress_barrier\"\n        tools:text=\"0:00\"\n        tools:visibility=\"gone\" />\n\n    <com.google.android.exoplayer2.ui.DefaultTimeBar\n        android:id=\"@+id/exo_progress\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/exo_duration\"\n        app:layout_constraintStart_toEndOf=\"@id/exo_position\"\n        app:layout_constraintTop_toBottomOf=\"@id/progress_barrier\"\n        app:layout_constraintVertical_bias=\"1\"\n        app:layout_constraintVertical_chainStyle=\"packed\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/exo_duration\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        android:textColor=\"@color/white\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/exo_progress\"\n        app:layout_constraintTop_toBottomOf=\"@id/progress_barrier\"\n        tools:text=\"0:00\"\n        tools:visibility=\"gone\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_gif_picker.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <View\n        android:id=\"@+id/input_bg\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginEnd=\"4dp\"\n        android:background=\"@drawable/bg_input\"\n        app:layout_constraintBottom_toBottomOf=\"@id/input\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/input\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/search_icon\"\n        style=\"@style/Widget.MaterialComponents.Button.Icon.NoInsets\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"2dp\"\n        android:background=\"@android:color/transparent\"\n        android:clickable=\"false\"\n        android:scrollbars=\"none\"\n        app:icon=\"@drawable/ic_search_24\"\n        app:iconGravity=\"textStart\"\n        app:iconSize=\"24dp\"\n        app:iconTint=\"@color/grey_700\"\n        app:layout_constraintBottom_toBottomOf=\"@id/input_bg\"\n        app:layout_constraintEnd_toStartOf=\"@id/input\"\n        app:layout_constraintStart_toStartOf=\"@id/input_bg\"\n        app:layout_constraintTop_toTopOf=\"@id/input\"\n        app:rippleColor=\"@color/grey_500\"\n        app:shapeAppearanceOverlay=\"@style/ShapeAppearanceOverlay.App.Button.Circle\"\n        app:strokeColor=\"@color/black\"\n        app:strokeWidth=\"1dp\" />\n\n    <androidx.emoji.widget.EmojiAppCompatEditText\n        android:id=\"@+id/input\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"4dp\"\n        android:layout_marginTop=\"4dp\"\n        android:layout_marginEnd=\"20dp\"\n        android:layout_marginBottom=\"4dp\"\n        android:background=\"@android:color/transparent\"\n        android:hint=\"@string/search_giphy\"\n        android:paddingTop=\"12dp\"\n        android:paddingBottom=\"12dp\"\n        android:textColorHint=\"@color/grey_500\"\n        app:layout_constraintBottom_toTopOf=\"@id/gif_list\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/search_icon\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/gif_list\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/input\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_hashtag_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?toolbarColor\"\n    android:padding=\"@dimen/profile_info_container_bottom_space\">\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/mainHashtagImage\"\n        android:layout_width=\"@dimen/profile_picture_size\"\n        android:layout_height=\"@dimen/profile_picture_size\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/mainTagPostCount\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/mainTagPostCount\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:gravity=\"center\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        app:layout_constraintBottom_toTopOf=\"@id/fav_chip\"\n        app:layout_constraintStart_toEndOf=\"@id/mainHashtagImage\"\n        app:layout_constraintTop_toTopOf=\"@id/mainHashtagImage\"\n        tools:text=\"35 Posts\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnFollowTag\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"@string/follow\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_person_add_24\"\n        app:chipIconTint=\"@color/deep_purple_800\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainHashtagImage\"\n        app:layout_constraintStart_toEndOf=\"@id/mainHashtagImage\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainTagPostCount\"\n        app:rippleColor=\"@color/purple_200\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/fav_chip\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"@string/add_to_favorites\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_star_plus_24\"\n        app:chipIconTint=\"@color/yellow_800\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainHashtagImage\"\n        app:layout_constraintStart_toEndOf=\"@id/btnFollowTag\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainTagPostCount\"\n        app:rippleColor=\"@color/yellow_400\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_include_custom_format_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginBottom=\"8dp\"\n    tools:viewBindingIgnore=\"true\">\n\n    <LinearLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        tools:ignore=\"UselessParent\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1dip\"\n            android:background=\"?android:attr/dividerVertical\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"'ABCXYZ'\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"ABCXYZ\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"y/yy/yyyy\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"y=2020, yy=20, yyyy=2020\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"M/MM/MMM/MMMM\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"M=6, MM=06, MMM=Jun, MMMM=June\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"d/dd\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"d=2, dd=02\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"E/EEEE\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"E=Mon, EEEE=Monday\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"a\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"a=A/P, aa=AM/PM\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"H/HH (0-23)\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"H=6, HH=06\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"h/hh (0-12)\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"h=7, hh=07\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"m/mm\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"m=8, mm=08\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\"\n            tools:ignore=\"HardcodedText\">\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"0.5\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"s/ss\"\n                android:textColor=\"?android:textColorPrimary\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center_vertical\"\n                android:padding=\"8dp\"\n                android:text=\"s=9, ss=09\"\n                android:textColor=\"?android:textColorPrimary\" />\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1dip\"\n            android:background=\"?android:attr/dividerVertical\" />\n    </LinearLayout>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_include_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/toolbar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?actionBarSize\"\n    android:background=\"@null\"\n    app:title=\"@string/app_name\">\n</androidx.appcompat.widget.Toolbar>"
  },
  {
    "path": "app/src/main/res/layout/layout_location_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:animateLayoutChanges=\"true\"\n    android:background=\"?toolbarColor\"\n    android:padding=\"8dp\">\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/mainLocationImage\"\n        android:layout_width=\"@dimen/profile_picture_size\"\n        android:layout_height=\"@dimen/profile_picture_size\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnMap\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:background=\"@mipmap/ic_launcher\" />\n\n    <!--    <androidx.appcompat.widget.AppCompatTextView-->\n    <!--        android:id=\"@+id/mainLocPostCount\"-->\n    <!--        android:layout_width=\"0dp\"-->\n    <!--        android:layout_height=\"0dp\"-->\n    <!--        android:gravity=\"center_vertical\"-->\n    <!--        android:maxLines=\"1\"-->\n    <!--        android:paddingStart=\"12dp\"-->\n    <!--        android:paddingEnd=\"12dp\"-->\n    <!--        android:textAppearance=\"@style/TextAppearance.AppCompat\"-->\n    <!--        app:layout_constraintBottom_toTopOf=\"@id/btnMap\"-->\n    <!--        app:layout_constraintEnd_toEndOf=\"parent\"-->\n    <!--        app:layout_constraintStart_toEndOf=\"@id/mainLocationImage\"-->\n    <!--        app:layout_constraintTop_toTopOf=\"parent\"-->\n    <!--        tools:text=\"35 Posts\" />-->\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnMap\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"@string/map\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_map_24\"\n        app:chipIconTint=\"@color/green_500\"\n        app:layout_constraintStart_toEndOf=\"@id/mainLocationImage\"\n        app:layout_constraintTop_toTopOf=\"@id/mainLocationImage\"\n        app:rippleColor=\"@color/grey_500\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/fav_chip\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"@string/add_to_favorites\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_star_plus_24\"\n        app:chipIconTint=\"@color/yellow_800\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainLocationImage\"\n        app:layout_constraintStart_toEndOf=\"@id/mainLocationImage\"\n        app:layout_constraintTop_toBottomOf=\"@id/btnMap\"\n        app:rippleColor=\"@color/yellow_400\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/locationFullName\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:paddingStart=\"8dp\"\n        android:paddingLeft=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingRight=\"8dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toTopOf=\"@id/locationBiography\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainLocationImage\"\n        tools:text=\"OUR HOUSE\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/locationBiography\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?android:selectableItemBackground\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/locationFullName\"\n        tools:text=\"IN THE MIDDLE OF OUR STREET\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_post_view_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:parentTag=\"androidx.constraintlayout.widget.ConstraintLayout\">\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/buttons_top_barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"bottom\"\n        app:constraint_referenced_ids=\"post_container\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/like\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"36dp\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_not_liked\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"22dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/likes_count\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\"\n        app:layout_constraintVertical_bias=\"0\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        tools:visibility=\"visible\" />\n\n    <awais.instagrabber.customviews.FormattedNumberTextView\n        android:id=\"@+id/likes_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:layout_marginEnd=\"0dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:gravity=\"center_vertical\"\n        android:maxWidth=\"100dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"?android:attr/textColorSecondary\"\n        app:layout_constraintBottom_toBottomOf=\"@id/like\"\n        app:layout_constraintEnd_toStartOf=\"@id/comment\"\n        app:layout_constraintStart_toEndOf=\"@id/like\"\n        app:layout_constraintTop_toTopOf=\"@id/like\"\n        tools:text=\"9.9k\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/comment\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"36dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginStart=\"8dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        app:icon=\"@drawable/ic_outline_comments_24\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"22dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/comments_count\"\n        app:layout_constraintStart_toEndOf=\"@id/likes_count\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\" />\n\n    <awais.instagrabber.customviews.FormattedNumberTextView\n        android:id=\"@+id/comments_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center_vertical\"\n        android:maxWidth=\"100dp\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        android:textColor=\"?android:attr/textColorSecondary\"\n        app:layout_constraintBottom_toBottomOf=\"@id/comment\"\n        app:layout_constraintEnd_toStartOf=\"@id/space_1\"\n        app:layout_constraintStart_toEndOf=\"@id/comment\"\n        app:layout_constraintTop_toTopOf=\"@id/comment\"\n        tools:text=\"9999999999999999999999999999999999999999\" />\n\n    <Space\n        android:id=\"@+id/space_1\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"40dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/share\"\n        app:layout_constraintStart_toEndOf=\"@id/comments_count\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/share\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        app:icon=\"?attr/actionModeShareDrawable\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"18dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/save\"\n        app:layout_constraintStart_toEndOf=\"@id/space_1\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/save\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_round_bookmark_border_24\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"18dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/download\"\n        app:layout_constraintStart_toEndOf=\"@id/share\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.button.MaterialButton\n        android:id=\"@+id/download\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/background_grey_ripple\"\n        android:visibility=\"visible\"\n        app:icon=\"@drawable/ic_download\"\n        app:iconGravity=\"textStart\"\n        app:iconPadding=\"0dp\"\n        app:iconSize=\"18dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/buttons_bottom_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/save\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_top_barrier\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/buttons_bottom_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"top\"\n        app:constraint_referenced_ids=\"date,views_count\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/date\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/caption_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/views_count\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/buttons_bottom_barrier\"\n        tools:layout_constraintVertical_chainStyle=\"packed\"\n        tools:text=\"2020-11-07 11:18:55\"\n        tools:visibility=\"gone\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/views_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        app:layout_constraintBottom_toBottomOf=\"@id/date\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/date\"\n        app:layout_constraintTop_toTopOf=\"@id/date\"\n        tools:text=\"9999999999 views\"\n        tools:visibility=\"gone\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/caption_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierAllowsGoneWidgets=\"true\"\n        app:barrierDirection=\"bottom\"\n        app:constraint_referenced_ids=\"date,views_count\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/caption\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"@null\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:padding=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\"\n        app:layout_constraintBottom_toTopOf=\"@id/translate\"\n        app:layout_constraintTop_toBottomOf=\"@id/caption_barrier\"\n        app:layout_constraintVertical_bias=\"0\"\n        app:layout_constraintVertical_chainStyle=\"packed\"\n        tools:text=\"Text text text Text text text Text text text Text text text Text text text\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/translate\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@null\"\n        android:gravity=\"center_vertical\"\n        android:padding=\"8dp\"\n        android:text=\"@string/translate_caption\"\n        android:textColor=\"@color/blue_600\"\n        android:textSize=\"16sp\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/caption\" />\n</merge>"
  },
  {
    "path": "app/src/main/res/layout/layout_profile_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?colorSurface\">\n\n    <awais.instagrabber.customviews.CircularImageView\n        android:id=\"@+id/mainProfileImage\"\n        android:layout_width=\"@dimen/profile_picture_size\"\n        android:layout_height=\"@dimen/profile_picture_size\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:transitionName=\"profile_pic\"\n        android:visibility=\"invisible\"\n        app:actualImageScaleType=\"centerCrop\"\n        app:layout_constraintBottom_toTopOf=\"@id/top_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/btnFollow\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_bias=\"0\"\n        tools:foreground=\"@mipmap/ic_launcher\"\n        tools:visibility=\"visible\" />\n\n    <!-- for other people -->\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnFollow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/follow\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_person_add_24\"\n        app:chipIconTint=\"@color/deep_purple_200\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/fav_chip\"\n        app:layout_constraintStart_toEndOf=\"@id/mainProfileImage\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:rippleColor=\"@color/purple_200\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/mainStatus\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:clickable=\"false\"\n        android:gravity=\"center\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipMinHeight=\"40dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/fav_chip\"\n        app:layout_constraintStart_toEndOf=\"@id/btnFollow\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"omg what do u expect\"\n        tools:visibility=\"visible\" />\n\n    <!-- for user themself -->\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnSaved\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/saved\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_class_24\"\n        app:chipIconTint=\"@color/blue_700\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/fav_chip\"\n        app:layout_constraintStart_toEndOf=\"@id/mainStatus\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:rippleColor=\"@color/blue_A400\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnLiked\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/liked\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_like\"\n        app:chipIconTint=\"@color/red_600\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/fav_chip\"\n        app:layout_constraintStart_toEndOf=\"@id/btnSaved\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:rippleColor=\"@color/red_300\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/fav_chip\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/add_to_favorites\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_star_plus_24\"\n        app:chipIconTint=\"@color/yellow_800\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintStart_toEndOf=\"@id/mainProfileImage\"\n        app:layout_constraintTop_toBottomOf=\"@id/btnFollow\"\n        app:rippleColor=\"@color/yellow_400\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnTagged\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/tagged\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_outline_person_pin_24\"\n        app:chipIconTint=\"@color/deep_orange_800\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@+id/top_barrier\"\n        app:layout_constraintStart_toEndOf=\"@id/mainProfileImage\"\n        app:layout_constraintTop_toBottomOf=\"@id/fav_chip\"\n        app:rippleColor=\"@color/deep_orange_400\"\n        tools:visibility=\"visible\" />\n\n    <com.google.android.material.chip.Chip\n        android:id=\"@+id/btnDM\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/profile_chip_size\"\n        android:layout_marginStart=\"4dp\"\n        android:text=\"@string/dm_person\"\n        android:visibility=\"gone\"\n        app:chipBackgroundColor=\"@null\"\n        app:chipIcon=\"@drawable/ic_round_send_24\"\n        app:chipIconTint=\"@color/green\"\n        app:chipMinHeight=\"40dp\"\n        app:chipStartPadding=\"8dp\"\n        app:layout_constraintBottom_toTopOf=\"@+id/top_barrier\"\n        app:layout_constraintStart_toEndOf=\"@id/btnTagged\"\n        app:layout_constraintTop_toBottomOf=\"@id/fav_chip\"\n        app:rippleColor=\"@color/green\"\n        tools:visibility=\"visible\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/top_barrier\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:barrierDirection=\"bottom\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/mainFullName\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:ellipsize=\"marquee\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"4dp\"\n        android:paddingBottom=\"4dp\"\n        android:singleLine=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toTopOf=\"@id/mainBiography\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/top_barrier\"\n        tools:text=\"Austin Huang\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/isVerified\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"0dp\"\n        android:layout_gravity=\"center\"\n        android:adjustViewBounds=\"true\"\n        android:scaleType=\"fitCenter\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainFullName\"\n        app:layout_constraintStart_toEndOf=\"@id/mainFullName\"\n        app:layout_constraintTop_toTopOf=\"@id/mainFullName\"\n        app:srcCompat=\"@drawable/verified\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/isPrivate\"\n        android:layout_width=\"25dp\"\n        android:layout_height=\"0dp\"\n        android:layout_gravity=\"center\"\n        android:adjustViewBounds=\"true\"\n        android:scaleType=\"fitCenter\"\n        android:tint=\"@color/red_500\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainFullName\"\n        app:layout_constraintStart_toEndOf=\"@id/isVerified\"\n        app:layout_constraintTop_toTopOf=\"@id/mainFullName\"\n        app:srcCompat=\"@drawable/lock\"\n        tools:visibility=\"visible\" />\n\n    <CheckBox\n        android:id=\"@+id/fav_cb\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:button=\"@drawable/sl_favourite_24\"\n        android:visibility=\"gone\"\n        app:buttonTint=\"@color/yellow_800\"\n        app:layout_constraintBaseline_toBaselineOf=\"@id/mainFullName\"\n        app:layout_constraintBottom_toTopOf=\"@id/mainBiography\"\n        app:layout_constraintStart_toEndOf=\"@id/isVerified\" />\n\n    <ProgressBar\n        android:id=\"@+id/fav_progress\"\n        style=\"@style/Widget.MaterialComponents.CircularProgressIndicator\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"@id/mainFullName\"\n        app:layout_constraintStart_toEndOf=\"@id/isVerified\"\n        app:layout_constraintTop_toTopOf=\"@id/mainFullName\"\n        tools:visibility=\"gone\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/mainBiography\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?android:selectableItemBackground\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        app:layout_constraintBottom_toTopOf=\"@id/mainUrl\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainFullName\"\n        tools:text=\"One of THE GLORIOUS OWNERS OF THIS APP. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec id justo lorem. In malesuada feugiat ornare. Suspendisse et mauris imperdiet, luctus augue eget, tempus eros. Cras vitae molestie ipsum. \" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/mainUrl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/mainBiography\"\n        android:ellipsize=\"marquee\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/profileContext\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainBiography\"\n        tools:text=\"https://austinhuang.me/\"\n        tools:textColor=\"@android:color/holo_blue_dark\"\n        tools:visibility=\"visible\" />\n\n    <awais.instagrabber.customviews.RamboTextViewV2\n        android:id=\"@+id/profileContext\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/mainUrl\"\n        android:ellipsize=\"marquee\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"4dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        android:textSize=\"12sp\"\n        android:textStyle=\"italic\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toTopOf=\"@id/counts_divider\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/mainUrl\"\n        tools:text=\"Followed by @instagram, @facebook + 69 more\"\n        tools:visibility=\"visible\" />\n\n    <include\n        android:id=\"@+id/counts_divider\"\n        layout=\"@layout/item_pref_divider\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"1dp\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toTopOf=\"@id/mainPostCount\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/profileContext\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/mainPostCount\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        app:layout_constraintBottom_toTopOf=\"@id/highlights_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/mainFollowers\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/counts_divider\"\n        tools:text=\"35\\nPosts\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/mainFollowers\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        app:layout_constraintBottom_toTopOf=\"@id/highlights_barrier\"\n        app:layout_constraintEnd_toStartOf=\"@id/mainFollowing\"\n        app:layout_constraintStart_toEndOf=\"@id/mainPostCount\"\n        app:layout_constraintTop_toBottomOf=\"@id/counts_divider\"\n        tools:text=\"68\\nFollowers\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/mainFollowing\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?selectableItemBackgroundBorderless\"\n        android:gravity=\"center\"\n        android:paddingStart=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:paddingEnd=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat\"\n        app:layout_constraintBottom_toTopOf=\"@id/highlights_barrier\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/mainFollowers\"\n        app:layout_constraintTop_toBottomOf=\"@id/counts_divider\"\n        tools:text=\"64\\nFollowing\" />\n\n    <androidx.constraintlayout.widget.Barrier\n        android:id=\"@+id/highlights_barrier\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:barrierDirection=\"bottom\"\n        app:constraint_referenced_ids=\"mainPostCount, mainFollowers, mainFollowing\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/highlightsList\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:clipToPadding=\"false\"\n        android:paddingStart=\"5dp\"\n        android:paddingLeft=\"5dp\"\n        android:paddingEnd=\"5dp\"\n        android:paddingRight=\"5dp\"\n        android:visibility=\"gone\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/highlights_barrier\"\n        tools:itemCount=\"2\"\n        tools:visibility=\"visible\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_searchview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.SearchView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\" />"
  },
  {
    "path": "app/src/main/res/layout/layout_video_player_with_thumbnail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ViewSwitcher xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:id=\"@+id/thumbnail_parent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/thumbnail\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:actualImageScaleType=\"fitCenter\"\n            app:viewAspectRatio=\"1\" />\n\n        <androidx.appcompat.widget.AppCompatImageView\n            android:layout_width=\"50dp\"\n            android:layout_height=\"50dp\"\n            android:layout_gravity=\"center\"\n            app:srcCompat=\"@drawable/ic_video_24\" />\n    </FrameLayout>\n\n    <!--app:controller_layout_id=\"@layout/layout_exo_custom_controls\"-->\n    <com.google.android.exoplayer2.ui.StyledPlayerView\n        android:id=\"@+id/playerView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"center\"\n        app:resize_mode=\"fixed_width\"\n        app:show_timeout=\"2000\"\n        app:use_controller=\"true\" />\n</ViewSwitcher>"
  },
  {
    "path": "app/src/main/res/layout/pref_account_switcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?attr/selectableItemBackground\"\n    android:clickable=\"true\"\n    android:focusable=\"true\"\n    android:padding=\"16dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/profile_pic\"\n        android:layout_width=\"@dimen/image_size_40\"\n        android:layout_height=\"@dimen/image_size_40\"\n        android:background=\"@android:color/transparent\"\n        android:scaleType=\"centerCrop\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:roundAsCircle=\"true\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/full_name\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@android:color/transparent\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"16dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Body1\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toTopOf=\"@id/username\"\n        app:layout_constraintEnd_toStartOf=\"@id/arrow_down\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"full name\" />\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/username\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@android:color/transparent\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"16dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingEnd=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toStartOf=\"@id/arrow_down\"\n        app:layout_constraintStart_toEndOf=\"@id/profile_pic\"\n        app:layout_constraintTop_toBottomOf=\"@id/full_name\"\n        tools:text=\"\\@username\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/arrow_down\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@android:color/transparent\"\n        android:padding=\"8dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/full_name\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_arrow_drop_down_24\"\n        app:tint=\"?colorAccent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/pref_auto_refresh_dm_freq.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:animateLayoutChanges=\"true\"\n    android:clickable=\"false\"\n    android:gravity=\"center_vertical\"\n    android:minHeight=\"?android:attr/listPreferredItemHeight\"\n    android:orientation=\"horizontal\"\n    android:paddingStart=\"15dp\"\n    android:paddingEnd=\"15dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/start_text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/auto_refresh_every\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body1\" />\n\n    <androidx.appcompat.widget.AppCompatEditText\n        android:id=\"@+id/freq_num\"\n        android:layout_width=\"50dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"2dp\"\n        android:layout_marginEnd=\"2dp\"\n        android:inputType=\"number\"\n        android:textAlignment=\"center\" />\n\n    <androidx.appcompat.widget.AppCompatSpinner\n        android:id=\"@+id/freq_unit\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/pref_more_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:scaleType=\"fitCenter\"\n        app:srcCompat=\"@mipmap/ic_launcher\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/record_view_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:clipChildren=\"false\"\n    android:clipToPadding=\"false\"\n    android:paddingTop=\"15dp\"\n    android:paddingBottom=\"15dp\">\n\n    <androidx.appcompat.widget.AppCompatTextView\n        android:id=\"@+id/slide_to_cancel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:includeFontPadding=\"false\"\n        android:scrollbars=\"none\"\n        android:textAppearance=\"@style/TextAppearance.MaterialComponents.Body2\"\n        app:drawableStartCompat=\"@drawable/recv_ic_arrow\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"Slide to cancel\"\n        tools:textColor=\"#444\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/glowing_mic\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/recv_ic_mic\"\n        tools:visibility=\"visible\" />\n\n    <androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id/basket_img\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:visibility=\"invisible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:tint=\"?attr/colorOnPrimary\"\n        tools:srcCompat=\"@drawable/recv_basket_animated\"\n        tools:visibility=\"visible\" />\n\n    <Chronometer\n        android:id=\"@+id/counter_tv\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:gravity=\"center\"\n        android:scrollbars=\"none\"\n        android:text=\"00:00\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/glowing_mic\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/layout-land/activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layoutDirection=\"ltr\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/camera_capture_button\"\n        android:layout_width=\"80dp\"\n        android:layout_height=\"80dp\"\n        android:layout_marginEnd=\"80dp\"\n        android:background=\"@android:color/transparent\"\n        android:elevation=\"2dp\"\n        android:scaleType=\"fitCenter\"\n        android:src=\"@drawable/ic_shutter\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\" />\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clickable=\"true\"\n        android:focusable=\"true\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/switch_camera\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginEnd=\"100dp\"\n        android:layout_marginBottom=\"40dp\"\n        android:background=\"@android:color/transparent\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_round_flip_camera_24\"\n        app:tint=\"@color/white\" />\n\n    <androidx.appcompat.widget.AppCompatImageButton\n        android:id=\"@+id/close\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_marginTop=\"40dp\"\n        android:layout_marginEnd=\"100dp\"\n        android:background=\"@android:color/transparent\"\n        android:scaleType=\"fitCenter\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_close_24\"\n        app:tint=\"@color/white\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "app/src/main/res/menu/bottom_nav_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@id/direct_messages_nav_graph\"\n        android:icon=\"@drawable/ic_message_24\"\n        android:contentDescription=\"@string/title_dm\"\n        android:title=\"@string/title_dm\" />\n    <item\n        android:id=\"@id/feed_nav_graph\"\n        android:icon=\"@drawable/ic_home_24\"\n        android:contentDescription=\"@string/feed\"\n        android:title=\"@string/feed\" />\n    <item\n        android:id=\"@id/profile_nav_graph\"\n        android:icon=\"@drawable/ic_person_24\"\n        android:contentDescription=\"@string/profile\"\n        android:title=\"@string/profile\" />\n    <item\n        android:id=\"@id/discover_nav_graph\"\n        android:icon=\"@drawable/ic_explore_24\"\n        android:contentDescription=\"@string/title_discover\"\n        android:title=\"@string/title_discover\" />\n    <item\n        android:id=\"@id/more_nav_graph\"\n        android:icon=\"@drawable/ic_more_horiz_24\"\n        android:contentDescription=\"@string/more\"\n        android:title=\"@string/more\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/collection_posts_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/edit\"\n        android:icon=\"@drawable/ic_round_edit_24\"\n        android:title=\"@string/edit_collection\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/delete\"\n        android:icon=\"@drawable/ic_delete\"\n        android:title=\"@string/delete_collection\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/comment_options_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/translate\"\n        android:title=\"@string/comment_viewer_translate_comment\" />\n    <item\n        android:id=\"@+id/delete\"\n        android:title=\"@string/delete\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/dm_inbox_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/pending_requests\"\n        android:icon=\"@drawable/ic_account_clock_24\"\n        android:title=\"@string/pending_requests\"\n        android:visible=\"false\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/dm_thread_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/refresh\"\n        android:icon=\"@drawable/ic_refresh_24\"\n        android:title=\"@string/refresh\"\n        app:showAsAction=\"ifRoom\" />\n    <item\n        android:id=\"@+id/mark_as_seen\"\n        android:icon=\"@drawable/ic_check_all_24\"\n        android:title=\"@string/mark_as_seen\"\n        app:showAsAction=\"ifRoom\" />\n    <item\n        android:id=\"@+id/info\"\n        android:icon=\"@drawable/ic_outline_info_24\"\n        android:title=\"@string/dm_thread_info\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/feed_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/storyList\"\n        android:icon=\"@drawable/ic_story_list\"\n        android:title=\"@string/feed_stories\"\n        android:visible=\"false\"\n        app:showAsAction=\"always\" />\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/follow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_search\"\n        android:actionLayout=\"@layout/layout_searchview\"\n        android:icon=\"@drawable/ic_search_24\"\n        android:title=\"@string/action_search\"\n        android:titleCondensed=\"@string/action_search\"\n        app:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        app:showAsAction=\"always|collapseActionView\" />\n\n    <item\n        android:id=\"@+id/action_compare\"\n        android:icon=\"@drawable/ic_outline_views_24\"\n        android:title=\"@string/action_compare\"\n        android:titleCondensed=\"@string/action_compare\"\n        app:showAsAction=\"ifRoom\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/hashtag_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/location_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/main_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/search\"\n        android:enabled=\"true\"\n        android:icon=\"@drawable/ic_search_24\"\n        android:title=\"@string/search\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"AlwaysShowAction\">\n\n    <item\n        android:id=\"@+id/action_download\"\n        android:icon=\"@drawable/ic_download\"\n        android:title=\"@string/action_download\"\n        android:titleCondensed=\"@string/action_download\"\n        android:visible=\"false\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_notif\"\n        android:icon=\"@drawable/ic_notif\"\n        android:title=\"@string/action_notif\"\n        android:titleCondensed=\"@string/action_notif\"\n        android:visible=\"false\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/action_about\"\n        android:icon=\"@android:drawable/ic_menu_info_details\"\n        android:title=\"@string/action_about\"\n        android:titleCondensed=\"@string/action_about\"\n        android:visible=\"false\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/action_settings\"\n        android:icon=\"@android:drawable/ic_menu_preferences\"\n        android:title=\"@string/action_settings\"\n        android:titleCondensed=\"@string/action_settings\"\n        android:visible=\"false\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/action_dms\"\n        android:icon=\"@android:drawable/ic_menu_send\"\n        android:title=\"@string/action_dms\"\n        android:titleCondensed=\"@string/action_dms\"\n        android:visible=\"false\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_search\"\n        android:actionLayout=\"@layout/layout_searchview\"\n        android:icon=\"@drawable/ic_search_24\"\n        android:title=\"@string/action_search\"\n        android:titleCondensed=\"@string/action_search\"\n        app:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        app:showAsAction=\"always|collapseActionView\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/multi_select_download_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n\n        android:id=\"@+id/action_download\"\n        android:icon=\"@drawable/ic_download\"\n        android:title=\"@string/action_download\"\n        android:titleCondensed=\"@string/action_download\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/post_view_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/edit_caption\"\n        android:title=\"@string/edit_caption\" />\n    <item\n        android:id=\"@+id/delete\"\n        android:title=\"@string/delete\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/profile_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/chaining\"\n        android:title=\"@string/action_ayml\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/block\"\n        android:title=\"@string/block\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/restrict\"\n        android:title=\"@string/restrict\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/remove_follower\"\n        android:title=\"@string/remove_follower\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/mute_stories\"\n        android:title=\"@string/mute_stories\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/mute_posts\"\n        android:title=\"@string/mute_posts\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/share_link\"\n        android:title=\"@string/share_link\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/share_dm\"\n        android:title=\"@string/share_via_dm\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/saved.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/downloadAction\"\n        android:icon=\"@drawable/ic_download\"\n        app:showAsAction=\"always|collapseActionView\" />\n\n    <item\n        android:id=\"@+id/favouriteAction\"\n        android:icon=\"@drawable/ic_not_liked\"\n        app:showAsAction=\"always|collapseActionView\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/saved_collection_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/add\"\n        android:icon=\"@drawable/ic_add\"\n        android:title=\"@string/saved_create_collection\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/saved_collection_select_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/action_add\"\n        android:icon=\"@drawable/ic_add\"\n        android:title=\"@string/add_to_collection\"\n        android:titleCondensed=\"@string/action_download\"\n        app:showAsAction=\"always\" />\n    <item\n        android:id=\"@+id/action_delete\"\n        android:icon=\"@drawable/ic_cancel\"\n        android:title=\"@string/remove_from_collection\"\n        android:titleCondensed=\"@string/action_download\"\n        app:showAsAction=\"always\" />\n    <item\n        android:id=\"@+id/action_download\"\n        android:icon=\"@drawable/ic_download\"\n        android:title=\"@string/action_download\"\n        android:titleCondensed=\"@string/action_download\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/saved_viewer_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_search\"\n        android:actionLayout=\"@layout/layout_searchview\"\n        android:icon=\"@drawable/ic_search_24\"\n        android:title=\"@string/action_search\"\n        android:titleCondensed=\"@string/action_search\"\n        app:actionViewClass=\"androidx.appcompat.widget.SearchView\"\n        app:showAsAction=\"always|collapseActionView\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/speed_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/pt_two_five_x\"\n        android:title=\"@string/pt_two_five_x\" />\n\n    <item\n        android:id=\"@+id/pt_five_x\"\n        android:title=\"@string/pt_five_x\" />\n    <item\n        android:id=\"@+id/pt_seven_five_x\"\n        android:title=\"@string/pt_seven_five_x\" />\n    <item\n        android:id=\"@+id/one_x\"\n        android:title=\"@string/one_x\" />\n\n    <item\n        android:id=\"@+id/one_pt_two_five_x\"\n        android:title=\"@string/one_pt_two_five_x\" />\n\n    <item\n        android:id=\"@+id/one_pt_five_x\"\n        android:title=\"@string/one_pt_five_x\" />\n    <item\n        android:id=\"@+id/two_x\"\n        android:title=\"@string/two_x\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/story_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_profile\"\n        android:title=\"@string/open_profile\"\n        android:titleCondensed=\"@string/open_profile\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/topic_posts_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/layout\"\n        android:title=\"@string/layout\"\n        app:showAsAction=\"never\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@mipmap/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@mipmap/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/navigation/direct_messages_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/direct_messages_nav_graph\"\n    app:startDestination=\"@id/directMessagesInboxFragment\">\n\n    <fragment\n        android:id=\"@+id/directMessagesInboxFragment\"\n        android:name=\"awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment\"\n        android:label=\"@string/action_dms\"\n        tools:layout=\"@layout/fragment_direct_messages_inbox\">\n\n        <action\n            android:id=\"@+id/action_to_thread\"\n            app:destination=\"@id/directMessagesThreadFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_pending_inbox\"\n            app:destination=\"@id/directPendingInboxFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/directMessagesThreadFragment\"\n        android:name=\"awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment\"\n        tools:layout=\"@layout/fragment_direct_messages_thread\">\n\n        <argument\n            android:name=\"threadId\"\n            app:argType=\"string\" />\n\n        <argument\n            android:name=\"title\"\n            app:argType=\"string\" />\n\n        <argument\n            android:name=\"pending\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <deepLink app:uri=\"barinsta://dm_thread/{threadId}/{title}?pending={pending}\" />\n\n        <action\n            android:id=\"@+id/action_to_settings\"\n            app:destination=\"@id/directMessagesSettingsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_image_edit\"\n            app:destination=\"@id/imageEditFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/directMessagesSettingsFragment\"\n        android:name=\"awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment\"\n        android:label=\"@string/details\"\n        tools:layout=\"@layout/fragment_direct_messages_settings\">\n        <argument\n            android:name=\"threadId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"title\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"pending\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_inbox\"\n            app:destination=\"@id/directMessagesInboxFragment\"\n            app:popUpTo=\"@+id/directMessagesInboxFragment\"\n            app:popUpToInclusive=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/imageEditFragment\"\n        android:name=\"awais.instagrabber.fragments.imageedit.ImageEditFragment\"\n        android:label=\"Edit Photo\"\n        tools:layout=\"@layout/fragment_image_edit\">\n        <argument\n            android:name=\"uri\"\n            app:argType=\"android.net.Uri\"\n            app:nullable=\"false\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/directPendingInboxFragment\"\n        android:name=\"awais.instagrabber.fragments.directmessages.DirectPendingInboxFragment\"\n        android:label=\"@string/pending_requests\"\n        tools:layout=\"@layout/fragment_direct_pending_inbox\">\n        <action\n            android:id=\"@+id/action_to_thread\"\n            app:destination=\"@id/directMessagesThreadFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/discover_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/discover_nav_graph\"\n    app:startDestination=\"@id/discoverFragment\">\n\n    <fragment\n        android:id=\"@+id/discoverFragment\"\n        android:name=\"awais.instagrabber.fragments.main.DiscoverFragment\"\n        android:label=\"@string/title_discover\"\n        tools:layout=\"@layout/fragment_discover\">\n\n        <argument\n            android:name=\"keyword\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/favorites_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/favorites_nav_graph\"\n    app:startDestination=\"@id/favoritesFragment\">\n\n    <fragment\n        android:id=\"@+id/favoritesFragment\"\n        android:name=\"awais.instagrabber.fragments.FavoritesFragment\"\n        android:label=\"@string/title_favorites\"\n        tools:layout=\"@layout/fragment_favorites\">\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/feed_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/feed_nav_graph\"\n    app:startDestination=\"@id/feedFragment\">\n\n    <fragment\n        android:id=\"@+id/feedFragment\"\n        android:name=\"awais.instagrabber.fragments.main.FeedFragment\"\n        android:label=\"@string/feed\"\n        tools:layout=\"@layout/fragment_feed\">\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story_list\"\n            app:destination=\"@id/storyListViewerFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyListViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryListViewerFragment\"\n        android:label=\"Stories\"\n        tools:layout=\"@layout/fragment_story_list_viewer\">\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/more_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/more_nav_graph\"\n    app:startDestination=\"@id/morePreferencesFragment\">\n\n    <fragment\n        android:id=\"@+id/morePreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.MorePreferencesFragment\"\n        android:label=\"@string/more\">\n\n        <action\n            android:id=\"@+id/action_to_settings\"\n            app:destination=\"@id/settings_nav_graph\" />\n\n        <action\n            android:id=\"@+id/action_to_discover\"\n            app:destination=\"@id/discover_nav_graph\" />\n\n        <action\n            android:id=\"@+id/action_to_about\"\n            app:destination=\"@id/aboutFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_favorites\"\n            app:destination=\"@id/favorites_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_backup\"\n            app:destination=\"@id/backupPreferencesFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_story_list\"\n            app:destination=\"@id/storyListViewerFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of favorites fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/favorites_non_top\"\n        android:name=\"awais.instagrabber.fragments.FavoritesFragment\"\n        android:label=\"@string/title_favorites\"\n        tools:layout=\"@layout/fragment_favorites\">\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n    </fragment>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n\n    <include app:graph=\"@navigation/settings_nav_graph\" />\n\n    <include app:graph=\"@navigation/discover_nav_graph\" />\n\n    <fragment\n        android:id=\"@+id/storyListViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryListViewerFragment\"\n        android:label=\"Stories\"\n        tools:layout=\"@layout/fragment_story_list_viewer\">\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/aboutFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.AboutFragment\"\n        android:label=\"@string/action_about\" />\n    <fragment\n        android:id=\"@+id/backupPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.BackupPreferencesFragment\"\n        android:label=\"@string/backup_and_restore\" />\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/notification_viewer_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/notification_viewer_nav_graph\"\n    app:startDestination=\"@id/notificationsViewer\">\n\n    <fragment\n        android:id=\"@+id/notificationsViewer\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/profile_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/profile_nav_graph\"\n    app:startDestination=\"@id/profileFragment\">\n\n    <fragment\n        android:id=\"@+id/profileFragment\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/post_loading_dialog\"\n        android:name=\"awais.instagrabber.dialogs.PostLoadingDialogFragment\"\n        android:label=\"@string/direct_download_loading\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\" />\n\n        <deepLink app:uri=\"barinsta://post/{shortCode}\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </dialog>\n\n    <fragment\n        android:id=\"@+id/storyViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.StoryViewerFragment\"\n        android:label=\"StoryViewerFragment\"\n        tools:layout=\"@layout/fragment_story_viewer\">\n\n        <argument\n            android:name=\"options\"\n            app:argType=\"awais.instagrabber.repositories.requests.StoryViewerOptions\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/searchFragment\"\n        android:name=\"awais.instagrabber.fragments.search.SearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_search\">\n\n        <deepLink app:uri=\"barinsta://search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/postViewFragment\"\n        android:name=\"awais.instagrabber.fragments.PostViewV2Fragment\"\n        android:label=\"@string/post\"\n        tools:layout=\"@layout/dialog_post_view\">\n\n        <argument\n            android:name=\"media\"\n            app:argType=\"awais.instagrabber.repositories.responses.Media\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"position\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/locationFragment\"\n        android:name=\"awais.instagrabber.fragments.LocationFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_location\">\n\n        <argument\n            android:name=\"locationId\"\n            app:argType=\"long\" />\n\n        <deepLink app:uri=\"barinsta://location/{locationId}\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/hashTagFragment\"\n        android:name=\"awais.instagrabber.fragments.HashTagFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_hashtag\">\n\n        <argument\n            android:name=\"hashtag\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <deepLink app:uri=\"barinsta://hashtag/{hashtag}\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/commentsViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.comments.CommentsViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_comments\">\n\n        <argument\n            android:name=\"shortCode\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"postUserId\"\n            app:argType=\"long\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_likes\"\n            app:destination=\"@id/likesViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of profile fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/profile_non_top\"\n        android:name=\"awais.instagrabber.fragments.main.ProfileFragment\"\n        android:label=\"@string/profile\"\n        tools:layout=\"@layout/fragment_profile\">\n\n        <argument\n            android:name=\"username\"\n            android:defaultValue=\"\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <deepLink app:uri=\"barinsta://profile/{username}\" />\n\n        <action\n            android:id=\"@+id/action_to_saved\"\n            app:destination=\"@id/savedViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_saved_collections\"\n            app:destination=\"@id/savedCollectionsFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_follow_viewer\"\n            app:destination=\"@id/followViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_user_search\"\n            app:destination=\"@id/user_search\" />\n\n        <action\n            android:id=\"@+id/action_to_notifications\"\n            app:destination=\"@id/notifications_viewer_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedViewerFragment\"\n        android:label=\"Saved\"\n        tools:layout=\"@layout/fragment_saved\">\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"type\"\n            app:argType=\"awais.instagrabber.models.enums.PostItemType\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/followViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.FollowViewerFragment\"\n        android:label=\"\"\n        tools:layout=\"@layout/fragment_followers_viewer\">\n\n        <argument\n            android:name=\"profileId\"\n            app:argType=\"long\" />\n\n        <argument\n            android:name=\"isFollowersList\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"username\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <dialog\n        android:id=\"@+id/likesViewerFragment\"\n        android:name=\"awais.instagrabber.fragments.LikesViewerFragment\"\n        android:label=\"Comments\"\n        tools:layout=\"@layout/fragment_likes\">\n\n        <argument\n            android:name=\"postId\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"isComment\"\n            app:argType=\"boolean\"\n            app:nullable=\"false\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </dialog>\n\n    <!-- Copy of notification viewer fragment tag -->\n    <!-- Required to get back arrow in action bar -->\n    <!-- See https://issuetracker.google.com/issues/192395936 -->\n    <fragment\n        android:id=\"@+id/notifications_viewer_non_top\"\n        android:name=\"awais.instagrabber.fragments.NotificationsViewerFragment\"\n        android:label=\"@string/title_notifications\"\n        tools:layout=\"@layout/fragment_notifications_viewer\">\n\n        <argument\n            android:name=\"type\"\n            android:defaultValue=\"notif\"\n            app:argType=\"string\"\n            app:nullable=\"false\" />\n\n        <argument\n            android:name=\"targetId\"\n            android:defaultValue=\"0L\"\n            app:argType=\"long\" />\n\n        <deepLink app:uri=\"barinsta://notifications/{type}?targetId={targetId}\" />\n\n        <action\n            android:id=\"@+id/action_to_story\"\n            app:destination=\"@id/storyViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/savedCollectionsFragment\"\n        android:name=\"awais.instagrabber.fragments.SavedCollectionsFragment\"\n        android:label=\"@string/saved\"\n        tools:layout=\"@layout/fragment_saved_collections\">\n\n        <argument\n            android:name=\"isSaving\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <action\n            android:id=\"@+id/action_to_collection_posts\"\n            app:destination=\"@id/collectionPostsFragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/collectionPostsFragment\"\n        android:name=\"awais.instagrabber.fragments.CollectionPostsFragment\"\n        tools:layout=\"@layout/fragment_collection_posts\">\n\n        <argument\n            android:name=\"savedCollection\"\n            app:argType=\"awais.instagrabber.repositories.responses.saved.SavedCollection\" />\n\n        <argument\n            android:name=\"titleColor\"\n            app:argType=\"integer\" />\n\n        <argument\n            android:name=\"backgroundColor\"\n            app:argType=\"integer\" />\n\n        <action\n            android:id=\"@+id/action_to_comments\"\n            app:destination=\"@id/commentsViewerFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_hashtag\"\n            app:destination=\"@id/hashTagFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_location\"\n            app:destination=\"@id/locationFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_post\"\n            app:destination=\"@id/postViewFragment\" />\n\n        <action\n            android:id=\"@+id/action_to_profile\"\n            app:destination=\"@id/profile_non_top\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/user_search\"\n        android:name=\"awais.instagrabber.fragments.UserSearchFragment\"\n        android:label=\"@string/search\"\n        tools:layout=\"@layout/fragment_user_search\">\n        <argument\n            android:name=\"multiple\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"title\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"action_label\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"show_groups\"\n            android:defaultValue=\"false\"\n            app:argType=\"boolean\" />\n\n        <argument\n            android:name=\"search_mode\"\n            android:defaultValue=\"USER_SEARCH\"\n            app:argType=\"awais.instagrabber.fragments.UserSearchMode\" />\n\n        <argument\n            android:name=\"hideUserIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"long[]\"\n            app:nullable=\"true\" />\n\n        <argument\n            android:name=\"hideThreadIds\"\n            android:defaultValue=\"@null\"\n            app:argType=\"string[]\"\n            app:nullable=\"true\" />\n    </fragment>\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/root_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation>\n\n    <!--android:id=\"@+id/root\"-->\n    <!--app:startDestination=\"@id/profile_nav_graph\"-->\n\n    <!--<include app:graph=\"@navigation/dm_nav_graph\" />-->\n    <!--<include app:graph=\"@navigation/feed_nav_graph\" />-->\n    <!--<include app:graph=\"@navigation/profile_nav_graph\" />-->\n    <!--<include app:graph=\"@navigation/discover_nav_graph\" />-->\n    <!--<include app:graph=\"@navigation/more_nav_graph\" />-->\n</navigation>"
  },
  {
    "path": "app/src/main/res/navigation/settings_nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/settings_nav_graph\"\n    app:startDestination=\"@id/settingsPreferencesFragment\">\n\n    <fragment\n        android:id=\"@+id/settingsPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.SettingsPreferencesFragment\"\n        android:label=\"@string/action_settings\">\n        <action\n            android:id=\"@+id/action_settings_to_theme\"\n            app:destination=\"@id/themePreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_locale\"\n            app:destination=\"@id/localePreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_general\"\n            app:destination=\"@id/generalPreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_downloads\"\n            app:destination=\"@id/downloadsPreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_dm\"\n            app:destination=\"@id/DMPreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_stories\"\n            app:destination=\"@id/storiesPreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_notifications\"\n            app:destination=\"@id/notificationsPreferencesFragment\" />\n        <action\n            android:id=\"@+id/action_settings_to_post\"\n            app:destination=\"@id/postPreferencesFragment\" />\n    </fragment>\n    <fragment\n        android:id=\"@+id/themePreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.ThemePreferencesFragment\"\n        android:label=\"@string/theme_settings\" />\n    <fragment\n        android:id=\"@+id/localePreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.LocalePreferencesFragment\"\n        android:label=\"@string/pref_category_locale\" />\n    <fragment\n        android:id=\"@+id/generalPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.GeneralPreferencesFragment\"\n        android:label=\"@string/pref_category_general\" />\n    <fragment\n        android:id=\"@+id/downloadsPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.DownloadsPreferencesFragment\"\n        android:label=\"@string/pref_category_downloads\" />\n    <fragment\n        android:id=\"@+id/DMPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.DMPreferencesFragment\"\n        android:label=\"@string/pref_category_dm\" />\n    <fragment\n        android:id=\"@+id/storiesPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.StoriesPreferencesFragment\"\n        android:label=\"@string/pref_category_stories\" />\n    <fragment\n        android:id=\"@+id/notificationsPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.NotificationsPreferencesFragment\"\n        android:label=\"@string/pref_category_notifications\" />\n    <fragment\n        android:id=\"@+id/postPreferencesFragment\"\n        android:name=\"awais.instagrabber.fragments.settings.PostPreferencesFragment\"\n        android:label=\"@string/pref_category_post\" />\n</navigation>"
  },
  {
    "path": "app/src/main/res/raw/emojis.json",
    "content": "{\"SMILEYS_AND_EMOTION\":{\"type\":\"SMILEYS_AND_EMOTION\",\"emojis\":{\"😀\":{\"unicode\":\"😀\",\"name\":\"grinning face\",\"variants\":[]},\"😃\":{\"unicode\":\"😃\",\"name\":\"grinning face with big eyes\",\"variants\":[]},\"😄\":{\"unicode\":\"😄\",\"name\":\"grinning face with smiling eyes\",\"variants\":[]},\"😁\":{\"unicode\":\"😁\",\"name\":\"beaming face with smiling eyes\",\"variants\":[]},\"😆\":{\"unicode\":\"😆\",\"name\":\"grinning squinting face\",\"variants\":[]},\"😅\":{\"unicode\":\"😅\",\"name\":\"grinning face with sweat\",\"variants\":[]},\"🤣\":{\"unicode\":\"🤣\",\"name\":\"rolling on the floor laughing\",\"variants\":[]},\"😂\":{\"unicode\":\"😂\",\"name\":\"face with tears of joy\",\"variants\":[]},\"🙂\":{\"unicode\":\"🙂\",\"name\":\"slightly smiling face\",\"variants\":[]},\"🙃\":{\"unicode\":\"🙃\",\"name\":\"upside-down face\",\"variants\":[]},\"😉\":{\"unicode\":\"😉\",\"name\":\"winking face\",\"variants\":[]},\"😊\":{\"unicode\":\"😊\",\"name\":\"smiling face with smiling eyes\",\"variants\":[]},\"😇\":{\"unicode\":\"😇\",\"name\":\"smiling face with halo\",\"variants\":[]},\"🥰\":{\"unicode\":\"🥰\",\"name\":\"smiling face with hearts\",\"variants\":[]},\"😍\":{\"unicode\":\"😍\",\"name\":\"smiling face with heart-eyes\",\"variants\":[]},\"🤩\":{\"unicode\":\"🤩\",\"name\":\"star-struck\",\"variants\":[]},\"😘\":{\"unicode\":\"😘\",\"name\":\"face blowing a kiss\",\"variants\":[]},\"😗\":{\"unicode\":\"😗\",\"name\":\"kissing face\",\"variants\":[]},\"☺️\":{\"unicode\":\"☺️\",\"name\":\"smiling face\",\"variants\":[]},\"😚\":{\"unicode\":\"😚\",\"name\":\"kissing face with closed eyes\",\"variants\":[]},\"😙\":{\"unicode\":\"😙\",\"name\":\"kissing face with smiling eyes\",\"variants\":[]},\"🥲\":{\"unicode\":\"🥲\",\"name\":\"smiling face with tear\",\"variants\":[]},\"😋\":{\"unicode\":\"😋\",\"name\":\"face savoring food\",\"variants\":[]},\"😛\":{\"unicode\":\"😛\",\"name\":\"face with tongue\",\"variants\":[]},\"😜\":{\"unicode\":\"😜\",\"name\":\"winking face with tongue\",\"variants\":[]},\"🤪\":{\"unicode\":\"🤪\",\"name\":\"zany face\",\"variants\":[]},\"😝\":{\"unicode\":\"😝\",\"name\":\"squinting face with tongue\",\"variants\":[]},\"🤑\":{\"unicode\":\"🤑\",\"name\":\"money-mouth face\",\"variants\":[]},\"🤗\":{\"unicode\":\"🤗\",\"name\":\"hugging face\",\"variants\":[]},\"🤭\":{\"unicode\":\"🤭\",\"name\":\"face with hand over mouth\",\"variants\":[]},\"🤫\":{\"unicode\":\"🤫\",\"name\":\"shushing face\",\"variants\":[]},\"🤔\":{\"unicode\":\"🤔\",\"name\":\"thinking face\",\"variants\":[]},\"🤐\":{\"unicode\":\"🤐\",\"name\":\"zipper-mouth face\",\"variants\":[]},\"🤨\":{\"unicode\":\"🤨\",\"name\":\"face with raised eyebrow\",\"variants\":[]},\"😐\":{\"unicode\":\"😐\",\"name\":\"neutral face\",\"variants\":[]},\"😑\":{\"unicode\":\"😑\",\"name\":\"expressionless face\",\"variants\":[]},\"😶\":{\"unicode\":\"😶\",\"name\":\"face without mouth\",\"variants\":[]},\"😏\":{\"unicode\":\"😏\",\"name\":\"smirking face\",\"variants\":[]},\"😒\":{\"unicode\":\"😒\",\"name\":\"unamused face\",\"variants\":[]},\"🙄\":{\"unicode\":\"🙄\",\"name\":\"face with rolling eyes\",\"variants\":[]},\"😬\":{\"unicode\":\"😬\",\"name\":\"grimacing face\",\"variants\":[]},\"🤥\":{\"unicode\":\"🤥\",\"name\":\"lying face\",\"variants\":[]},\"😌\":{\"unicode\":\"😌\",\"name\":\"relieved face\",\"variants\":[]},\"😔\":{\"unicode\":\"😔\",\"name\":\"pensive face\",\"variants\":[]},\"😪\":{\"unicode\":\"😪\",\"name\":\"sleepy face\",\"variants\":[]},\"🤤\":{\"unicode\":\"🤤\",\"name\":\"drooling face\",\"variants\":[]},\"😴\":{\"unicode\":\"😴\",\"name\":\"sleeping face\",\"variants\":[]},\"😷\":{\"unicode\":\"😷\",\"name\":\"face with medical mask\",\"variants\":[]},\"🤒\":{\"unicode\":\"🤒\",\"name\":\"face with thermometer\",\"variants\":[]},\"🤕\":{\"unicode\":\"🤕\",\"name\":\"face with head-bandage\",\"variants\":[]},\"🤢\":{\"unicode\":\"🤢\",\"name\":\"nauseated face\",\"variants\":[]},\"🤮\":{\"unicode\":\"🤮\",\"name\":\"face vomiting\",\"variants\":[]},\"🤧\":{\"unicode\":\"🤧\",\"name\":\"sneezing face\",\"variants\":[]},\"🥵\":{\"unicode\":\"🥵\",\"name\":\"hot face\",\"variants\":[]},\"🥶\":{\"unicode\":\"🥶\",\"name\":\"cold face\",\"variants\":[]},\"🥴\":{\"unicode\":\"🥴\",\"name\":\"woozy face\",\"variants\":[]},\"😵\":{\"unicode\":\"😵\",\"name\":\"dizzy face\",\"variants\":[]},\"🤯\":{\"unicode\":\"🤯\",\"name\":\"exploding head\",\"variants\":[]},\"🤠\":{\"unicode\":\"🤠\",\"name\":\"cowboy hat face\",\"variants\":[]},\"🥳\":{\"unicode\":\"🥳\",\"name\":\"partying face\",\"variants\":[]},\"🥸\":{\"unicode\":\"🥸\",\"name\":\"disguised face\",\"variants\":[]},\"😎\":{\"unicode\":\"😎\",\"name\":\"smiling face with sunglasses\",\"variants\":[]},\"🤓\":{\"unicode\":\"🤓\",\"name\":\"nerd face\",\"variants\":[]},\"🧐\":{\"unicode\":\"🧐\",\"name\":\"face with monocle\",\"variants\":[]},\"😕\":{\"unicode\":\"😕\",\"name\":\"confused face\",\"variants\":[]},\"😟\":{\"unicode\":\"😟\",\"name\":\"worried face\",\"variants\":[]},\"🙁\":{\"unicode\":\"🙁\",\"name\":\"slightly frowning face\",\"variants\":[]},\"☹️\":{\"unicode\":\"☹️\",\"name\":\"frowning face\",\"variants\":[]},\"😮\":{\"unicode\":\"😮\",\"name\":\"face with open mouth\",\"variants\":[]},\"😯\":{\"unicode\":\"😯\",\"name\":\"hushed face\",\"variants\":[]},\"😲\":{\"unicode\":\"😲\",\"name\":\"astonished face\",\"variants\":[]},\"😳\":{\"unicode\":\"😳\",\"name\":\"flushed face\",\"variants\":[]},\"🥺\":{\"unicode\":\"🥺\",\"name\":\"pleading face\",\"variants\":[]},\"😦\":{\"unicode\":\"😦\",\"name\":\"frowning face with open mouth\",\"variants\":[]},\"😧\":{\"unicode\":\"😧\",\"name\":\"anguished face\",\"variants\":[]},\"😨\":{\"unicode\":\"😨\",\"name\":\"fearful face\",\"variants\":[]},\"😰\":{\"unicode\":\"😰\",\"name\":\"anxious face with sweat\",\"variants\":[]},\"😥\":{\"unicode\":\"😥\",\"name\":\"sad but relieved face\",\"variants\":[]},\"😢\":{\"unicode\":\"😢\",\"name\":\"crying face\",\"variants\":[]},\"😭\":{\"unicode\":\"😭\",\"name\":\"loudly crying face\",\"variants\":[]},\"😱\":{\"unicode\":\"😱\",\"name\":\"face screaming in fear\",\"variants\":[]},\"😖\":{\"unicode\":\"😖\",\"name\":\"confounded face\",\"variants\":[]},\"😣\":{\"unicode\":\"😣\",\"name\":\"persevering face\",\"variants\":[]},\"😞\":{\"unicode\":\"😞\",\"name\":\"disappointed face\",\"variants\":[]},\"😓\":{\"unicode\":\"😓\",\"name\":\"downcast face with sweat\",\"variants\":[]},\"😩\":{\"unicode\":\"😩\",\"name\":\"weary face\",\"variants\":[]},\"😫\":{\"unicode\":\"😫\",\"name\":\"tired face\",\"variants\":[]},\"🥱\":{\"unicode\":\"🥱\",\"name\":\"yawning face\",\"variants\":[]},\"😤\":{\"unicode\":\"😤\",\"name\":\"face with steam from nose\",\"variants\":[]},\"😡\":{\"unicode\":\"😡\",\"name\":\"pouting face\",\"variants\":[]},\"😠\":{\"unicode\":\"😠\",\"name\":\"angry face\",\"variants\":[]},\"🤬\":{\"unicode\":\"🤬\",\"name\":\"face with symbols on mouth\",\"variants\":[]},\"😈\":{\"unicode\":\"😈\",\"name\":\"smiling face with horns\",\"variants\":[]},\"👿\":{\"unicode\":\"👿\",\"name\":\"angry face with horns\",\"variants\":[]},\"💀\":{\"unicode\":\"💀\",\"name\":\"skull\",\"variants\":[]},\"☠️\":{\"unicode\":\"☠️\",\"name\":\"skull and crossbones\",\"variants\":[]},\"💩\":{\"unicode\":\"💩\",\"name\":\"pile of poo\",\"variants\":[]},\"🤡\":{\"unicode\":\"🤡\",\"name\":\"clown face\",\"variants\":[]},\"👹\":{\"unicode\":\"👹\",\"name\":\"ogre\",\"variants\":[]},\"👺\":{\"unicode\":\"👺\",\"name\":\"goblin\",\"variants\":[]},\"👻\":{\"unicode\":\"👻\",\"name\":\"ghost\",\"variants\":[]},\"👽\":{\"unicode\":\"👽\",\"name\":\"alien\",\"variants\":[]},\"👾\":{\"unicode\":\"👾\",\"name\":\"alien monster\",\"variants\":[]},\"🤖\":{\"unicode\":\"🤖\",\"name\":\"robot\",\"variants\":[]},\"😺\":{\"unicode\":\"😺\",\"name\":\"grinning cat\",\"variants\":[]},\"😸\":{\"unicode\":\"😸\",\"name\":\"grinning cat with smiling eyes\",\"variants\":[]},\"😹\":{\"unicode\":\"😹\",\"name\":\"cat with tears of joy\",\"variants\":[]},\"😻\":{\"unicode\":\"😻\",\"name\":\"smiling cat with heart-eyes\",\"variants\":[]},\"😼\":{\"unicode\":\"😼\",\"name\":\"cat with wry smile\",\"variants\":[]},\"😽\":{\"unicode\":\"😽\",\"name\":\"kissing cat\",\"variants\":[]},\"🙀\":{\"unicode\":\"🙀\",\"name\":\"weary cat\",\"variants\":[]},\"😿\":{\"unicode\":\"😿\",\"name\":\"crying cat\",\"variants\":[]},\"😾\":{\"unicode\":\"😾\",\"name\":\"pouting cat\",\"variants\":[]},\"🙈\":{\"unicode\":\"🙈\",\"name\":\"see-no-evil monkey\",\"variants\":[]},\"🙉\":{\"unicode\":\"🙉\",\"name\":\"hear-no-evil monkey\",\"variants\":[]},\"🙊\":{\"unicode\":\"🙊\",\"name\":\"speak-no-evil monkey\",\"variants\":[]},\"💋\":{\"unicode\":\"💋\",\"name\":\"kiss mark\",\"variants\":[]},\"💌\":{\"unicode\":\"💌\",\"name\":\"love letter\",\"variants\":[]},\"💘\":{\"unicode\":\"💘\",\"name\":\"heart with arrow\",\"variants\":[]},\"💝\":{\"unicode\":\"💝\",\"name\":\"heart with ribbon\",\"variants\":[]},\"💖\":{\"unicode\":\"💖\",\"name\":\"sparkling heart\",\"variants\":[]},\"💗\":{\"unicode\":\"💗\",\"name\":\"growing heart\",\"variants\":[]},\"💓\":{\"unicode\":\"💓\",\"name\":\"beating heart\",\"variants\":[]},\"💞\":{\"unicode\":\"💞\",\"name\":\"revolving hearts\",\"variants\":[]},\"💕\":{\"unicode\":\"💕\",\"name\":\"two hearts\",\"variants\":[]},\"💟\":{\"unicode\":\"💟\",\"name\":\"heart decoration\",\"variants\":[]},\"❣️\":{\"unicode\":\"❣️\",\"name\":\"heart exclamation\",\"variants\":[]},\"💔\":{\"unicode\":\"💔\",\"name\":\"broken heart\",\"variants\":[]},\"❤️\":{\"unicode\":\"❤️\",\"name\":\"red heart\",\"variants\":[]},\"🧡\":{\"unicode\":\"🧡\",\"name\":\"orange heart\",\"variants\":[]},\"💛\":{\"unicode\":\"💛\",\"name\":\"yellow heart\",\"variants\":[]},\"💚\":{\"unicode\":\"💚\",\"name\":\"green heart\",\"variants\":[]},\"💙\":{\"unicode\":\"💙\",\"name\":\"blue heart\",\"variants\":[]},\"💜\":{\"unicode\":\"💜\",\"name\":\"purple heart\",\"variants\":[]},\"🤎\":{\"unicode\":\"🤎\",\"name\":\"brown heart\",\"variants\":[]},\"🖤\":{\"unicode\":\"🖤\",\"name\":\"black heart\",\"variants\":[]},\"🤍\":{\"unicode\":\"🤍\",\"name\":\"white heart\",\"variants\":[]},\"💯\":{\"unicode\":\"💯\",\"name\":\"hundred points\",\"variants\":[]},\"💢\":{\"unicode\":\"💢\",\"name\":\"anger symbol\",\"variants\":[]},\"💥\":{\"unicode\":\"💥\",\"name\":\"collision\",\"variants\":[]},\"💫\":{\"unicode\":\"💫\",\"name\":\"dizzy\",\"variants\":[]},\"💦\":{\"unicode\":\"💦\",\"name\":\"sweat droplets\",\"variants\":[]},\"💨\":{\"unicode\":\"💨\",\"name\":\"dashing away\",\"variants\":[]},\"🕳️\":{\"unicode\":\"🕳️\",\"name\":\"hole\",\"variants\":[]},\"💣\":{\"unicode\":\"💣\",\"name\":\"bomb\",\"variants\":[]},\"💬\":{\"unicode\":\"💬\",\"name\":\"speech balloon\",\"variants\":[]},\"👁️‍🗨️\":{\"unicode\":\"👁️‍🗨️\",\"name\":\"eye in speech bubble\",\"variants\":[]},\"🗨️\":{\"unicode\":\"🗨️\",\"name\":\"left speech bubble\",\"variants\":[]},\"🗯️\":{\"unicode\":\"🗯️\",\"name\":\"right anger bubble\",\"variants\":[]},\"💭\":{\"unicode\":\"💭\",\"name\":\"thought balloon\",\"variants\":[]},\"💤\":{\"unicode\":\"💤\",\"name\":\"zzz\",\"variants\":[]},\"👋\":{\"unicode\":\"👋\",\"name\":\"waving hand\",\"variants\":[{\"unicode\":\"👋🏻\",\"name\":\"waving hand: light skin tone\",\"variants\":[]},{\"unicode\":\"👋🏼\",\"name\":\"waving hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👋🏽\",\"name\":\"waving hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"👋🏾\",\"name\":\"waving hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👋🏿\",\"name\":\"waving hand: dark skin tone\",\"variants\":[]}]},\"🤚\":{\"unicode\":\"🤚\",\"name\":\"raised back of hand\",\"variants\":[{\"unicode\":\"🤚🏻\",\"name\":\"raised back of hand: light skin tone\",\"variants\":[]},{\"unicode\":\"🤚🏼\",\"name\":\"raised back of hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤚🏽\",\"name\":\"raised back of hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤚🏾\",\"name\":\"raised back of hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤚🏿\",\"name\":\"raised back of hand: dark skin tone\",\"variants\":[]}]},\"🖐️\":{\"unicode\":\"🖐️\",\"name\":\"hand with fingers splayed\",\"variants\":[]},\"✋\":{\"unicode\":\"✋\",\"name\":\"raised hand\",\"variants\":[{\"unicode\":\"✋🏻\",\"name\":\"raised hand: light skin tone\",\"variants\":[]},{\"unicode\":\"✋🏼\",\"name\":\"raised hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"✋🏽\",\"name\":\"raised hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"✋🏾\",\"name\":\"raised hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"✋🏿\",\"name\":\"raised hand: dark skin tone\",\"variants\":[]}]},\"🖖\":{\"unicode\":\"🖖\",\"name\":\"vulcan salute\",\"variants\":[{\"unicode\":\"🖖🏻\",\"name\":\"vulcan salute: light skin tone\",\"variants\":[]},{\"unicode\":\"🖖🏼\",\"name\":\"vulcan salute: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🖖🏽\",\"name\":\"vulcan salute: medium skin tone\",\"variants\":[]},{\"unicode\":\"🖖🏾\",\"name\":\"vulcan salute: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🖖🏿\",\"name\":\"vulcan salute: dark skin tone\",\"variants\":[]}]},\"👌\":{\"unicode\":\"👌\",\"name\":\"OK hand\",\"variants\":[{\"unicode\":\"👌🏻\",\"name\":\"OK hand: light skin tone\",\"variants\":[]},{\"unicode\":\"👌🏼\",\"name\":\"OK hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👌🏽\",\"name\":\"OK hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"👌🏾\",\"name\":\"OK hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👌🏿\",\"name\":\"OK hand: dark skin tone\",\"variants\":[]}]},\"🤌\":{\"unicode\":\"🤌\",\"name\":\"pinched fingers\",\"variants\":[{\"unicode\":\"🤌🏻\",\"name\":\"pinched fingers: light skin tone\",\"variants\":[]},{\"unicode\":\"🤌🏼\",\"name\":\"pinched fingers: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤌🏽\",\"name\":\"pinched fingers: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤌🏾\",\"name\":\"pinched fingers: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤌🏿\",\"name\":\"pinched fingers: dark skin tone\",\"variants\":[]}]},\"🤏\":{\"unicode\":\"🤏\",\"name\":\"pinching hand\",\"variants\":[{\"unicode\":\"🤏🏻\",\"name\":\"pinching hand: light skin tone\",\"variants\":[]},{\"unicode\":\"🤏🏼\",\"name\":\"pinching hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤏🏽\",\"name\":\"pinching hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤏🏾\",\"name\":\"pinching hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤏🏿\",\"name\":\"pinching hand: dark skin tone\",\"variants\":[]}]},\"✌️\":{\"unicode\":\"✌️\",\"name\":\"victory hand\",\"variants\":[]},\"🤞\":{\"unicode\":\"🤞\",\"name\":\"crossed fingers\",\"variants\":[{\"unicode\":\"🤞🏻\",\"name\":\"crossed fingers: light skin tone\",\"variants\":[]},{\"unicode\":\"🤞🏼\",\"name\":\"crossed fingers: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤞🏽\",\"name\":\"crossed fingers: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤞🏾\",\"name\":\"crossed fingers: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤞🏿\",\"name\":\"crossed fingers: dark skin tone\",\"variants\":[]}]},\"🤟\":{\"unicode\":\"🤟\",\"name\":\"love-you gesture\",\"variants\":[{\"unicode\":\"🤟🏻\",\"name\":\"love-you gesture: light skin tone\",\"variants\":[]},{\"unicode\":\"🤟🏼\",\"name\":\"love-you gesture: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤟🏽\",\"name\":\"love-you gesture: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤟🏾\",\"name\":\"love-you gesture: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤟🏿\",\"name\":\"love-you gesture: dark skin tone\",\"variants\":[]}]},\"🤘\":{\"unicode\":\"🤘\",\"name\":\"sign of the horns\",\"variants\":[{\"unicode\":\"🤘🏻\",\"name\":\"sign of the horns: light skin tone\",\"variants\":[]},{\"unicode\":\"🤘🏼\",\"name\":\"sign of the horns: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤘🏽\",\"name\":\"sign of the horns: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤘🏾\",\"name\":\"sign of the horns: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤘🏿\",\"name\":\"sign of the horns: dark skin tone\",\"variants\":[]}]},\"🤙\":{\"unicode\":\"🤙\",\"name\":\"call me hand\",\"variants\":[{\"unicode\":\"🤙🏻\",\"name\":\"call me hand: light skin tone\",\"variants\":[]},{\"unicode\":\"🤙🏼\",\"name\":\"call me hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤙🏽\",\"name\":\"call me hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤙🏾\",\"name\":\"call me hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤙🏿\",\"name\":\"call me hand: dark skin tone\",\"variants\":[]}]},\"👈\":{\"unicode\":\"👈\",\"name\":\"backhand index pointing left\",\"variants\":[{\"unicode\":\"👈🏻\",\"name\":\"backhand index pointing left: light skin tone\",\"variants\":[]},{\"unicode\":\"👈🏼\",\"name\":\"backhand index pointing left: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👈🏽\",\"name\":\"backhand index pointing left: medium skin tone\",\"variants\":[]},{\"unicode\":\"👈🏾\",\"name\":\"backhand index pointing left: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👈🏿\",\"name\":\"backhand index pointing left: dark skin tone\",\"variants\":[]}]},\"👉\":{\"unicode\":\"👉\",\"name\":\"backhand index pointing right\",\"variants\":[{\"unicode\":\"👉🏻\",\"name\":\"backhand index pointing right: light skin tone\",\"variants\":[]},{\"unicode\":\"👉🏼\",\"name\":\"backhand index pointing right: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👉🏽\",\"name\":\"backhand index pointing right: medium skin tone\",\"variants\":[]},{\"unicode\":\"👉🏾\",\"name\":\"backhand index pointing right: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👉🏿\",\"name\":\"backhand index pointing right: dark skin tone\",\"variants\":[]}]},\"👆\":{\"unicode\":\"👆\",\"name\":\"backhand index pointing up\",\"variants\":[{\"unicode\":\"👆🏻\",\"name\":\"backhand index pointing up: light skin tone\",\"variants\":[]},{\"unicode\":\"👆🏼\",\"name\":\"backhand index pointing up: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👆🏽\",\"name\":\"backhand index pointing up: medium skin tone\",\"variants\":[]},{\"unicode\":\"👆🏾\",\"name\":\"backhand index pointing up: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👆🏿\",\"name\":\"backhand index pointing up: dark skin tone\",\"variants\":[]}]},\"🖕\":{\"unicode\":\"🖕\",\"name\":\"middle finger\",\"variants\":[{\"unicode\":\"🖕🏻\",\"name\":\"middle finger: light skin tone\",\"variants\":[]},{\"unicode\":\"🖕🏼\",\"name\":\"middle finger: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🖕🏽\",\"name\":\"middle finger: medium skin tone\",\"variants\":[]},{\"unicode\":\"🖕🏾\",\"name\":\"middle finger: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🖕🏿\",\"name\":\"middle finger: dark skin tone\",\"variants\":[]}]},\"👇\":{\"unicode\":\"👇\",\"name\":\"backhand index pointing down\",\"variants\":[{\"unicode\":\"👇🏻\",\"name\":\"backhand index pointing down: light skin tone\",\"variants\":[]},{\"unicode\":\"👇🏼\",\"name\":\"backhand index pointing down: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👇🏽\",\"name\":\"backhand index pointing down: medium skin tone\",\"variants\":[]},{\"unicode\":\"👇🏾\",\"name\":\"backhand index pointing down: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👇🏿\",\"name\":\"backhand index pointing down: dark skin tone\",\"variants\":[]}]},\"☝️\":{\"unicode\":\"☝️\",\"name\":\"index pointing up\",\"variants\":[]},\"👍\":{\"unicode\":\"👍\",\"name\":\"thumbs up\",\"variants\":[{\"unicode\":\"👍🏻\",\"name\":\"thumbs up: light skin tone\",\"variants\":[]},{\"unicode\":\"👍🏼\",\"name\":\"thumbs up: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👍🏽\",\"name\":\"thumbs up: medium skin tone\",\"variants\":[]},{\"unicode\":\"👍🏾\",\"name\":\"thumbs up: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👍🏿\",\"name\":\"thumbs up: dark skin tone\",\"variants\":[]}]},\"👎\":{\"unicode\":\"👎\",\"name\":\"thumbs down\",\"variants\":[{\"unicode\":\"👎🏻\",\"name\":\"thumbs down: light skin tone\",\"variants\":[]},{\"unicode\":\"👎🏼\",\"name\":\"thumbs down: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👎🏽\",\"name\":\"thumbs down: medium skin tone\",\"variants\":[]},{\"unicode\":\"👎🏾\",\"name\":\"thumbs down: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👎🏿\",\"name\":\"thumbs down: dark skin tone\",\"variants\":[]}]},\"✊\":{\"unicode\":\"✊\",\"name\":\"raised fist\",\"variants\":[{\"unicode\":\"✊🏻\",\"name\":\"raised fist: light skin tone\",\"variants\":[]},{\"unicode\":\"✊🏼\",\"name\":\"raised fist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"✊🏽\",\"name\":\"raised fist: medium skin tone\",\"variants\":[]},{\"unicode\":\"✊🏾\",\"name\":\"raised fist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"✊🏿\",\"name\":\"raised fist: dark skin tone\",\"variants\":[]}]},\"👊\":{\"unicode\":\"👊\",\"name\":\"oncoming fist\",\"variants\":[{\"unicode\":\"👊🏻\",\"name\":\"oncoming fist: light skin tone\",\"variants\":[]},{\"unicode\":\"👊🏼\",\"name\":\"oncoming fist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👊🏽\",\"name\":\"oncoming fist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👊🏾\",\"name\":\"oncoming fist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👊🏿\",\"name\":\"oncoming fist: dark skin tone\",\"variants\":[]}]},\"🤛\":{\"unicode\":\"🤛\",\"name\":\"left-facing fist\",\"variants\":[{\"unicode\":\"🤛🏻\",\"name\":\"left-facing fist: light skin tone\",\"variants\":[]},{\"unicode\":\"🤛🏼\",\"name\":\"left-facing fist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤛🏽\",\"name\":\"left-facing fist: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤛🏾\",\"name\":\"left-facing fist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤛🏿\",\"name\":\"left-facing fist: dark skin tone\",\"variants\":[]}]},\"🤜\":{\"unicode\":\"🤜\",\"name\":\"right-facing fist\",\"variants\":[{\"unicode\":\"🤜🏻\",\"name\":\"right-facing fist: light skin tone\",\"variants\":[]},{\"unicode\":\"🤜🏼\",\"name\":\"right-facing fist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤜🏽\",\"name\":\"right-facing fist: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤜🏾\",\"name\":\"right-facing fist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤜🏿\",\"name\":\"right-facing fist: dark skin tone\",\"variants\":[]}]},\"👏\":{\"unicode\":\"👏\",\"name\":\"clapping hands\",\"variants\":[{\"unicode\":\"👏🏻\",\"name\":\"clapping hands: light skin tone\",\"variants\":[]},{\"unicode\":\"👏🏼\",\"name\":\"clapping hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👏🏽\",\"name\":\"clapping hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"👏🏾\",\"name\":\"clapping hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👏🏿\",\"name\":\"clapping hands: dark skin tone\",\"variants\":[]}]},\"🙌\":{\"unicode\":\"🙌\",\"name\":\"raising hands\",\"variants\":[{\"unicode\":\"🙌🏻\",\"name\":\"raising hands: light skin tone\",\"variants\":[]},{\"unicode\":\"🙌🏼\",\"name\":\"raising hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙌🏽\",\"name\":\"raising hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙌🏾\",\"name\":\"raising hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙌🏿\",\"name\":\"raising hands: dark skin tone\",\"variants\":[]}]},\"👐\":{\"unicode\":\"👐\",\"name\":\"open hands\",\"variants\":[{\"unicode\":\"👐🏻\",\"name\":\"open hands: light skin tone\",\"variants\":[]},{\"unicode\":\"👐🏼\",\"name\":\"open hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👐🏽\",\"name\":\"open hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"👐🏾\",\"name\":\"open hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👐🏿\",\"name\":\"open hands: dark skin tone\",\"variants\":[]}]},\"🤲\":{\"unicode\":\"🤲\",\"name\":\"palms up together\",\"variants\":[{\"unicode\":\"🤲🏻\",\"name\":\"palms up together: light skin tone\",\"variants\":[]},{\"unicode\":\"🤲🏼\",\"name\":\"palms up together: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤲🏽\",\"name\":\"palms up together: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤲🏾\",\"name\":\"palms up together: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤲🏿\",\"name\":\"palms up together: dark skin tone\",\"variants\":[]}]},\"🤝\":{\"unicode\":\"🤝\",\"name\":\"handshake\",\"variants\":[]},\"🙏\":{\"unicode\":\"🙏\",\"name\":\"folded hands\",\"variants\":[{\"unicode\":\"🙏🏻\",\"name\":\"folded hands: light skin tone\",\"variants\":[]},{\"unicode\":\"🙏🏼\",\"name\":\"folded hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙏🏽\",\"name\":\"folded hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙏🏾\",\"name\":\"folded hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙏🏿\",\"name\":\"folded hands: dark skin tone\",\"variants\":[]}]},\"✍️\":{\"unicode\":\"✍️\",\"name\":\"writing hand\",\"variants\":[]},\"💅\":{\"unicode\":\"💅\",\"name\":\"nail polish\",\"variants\":[{\"unicode\":\"💅🏻\",\"name\":\"nail polish: light skin tone\",\"variants\":[]},{\"unicode\":\"💅🏼\",\"name\":\"nail polish: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💅🏽\",\"name\":\"nail polish: medium skin tone\",\"variants\":[]},{\"unicode\":\"💅🏾\",\"name\":\"nail polish: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💅🏿\",\"name\":\"nail polish: dark skin tone\",\"variants\":[]}]},\"🤳\":{\"unicode\":\"🤳\",\"name\":\"selfie\",\"variants\":[{\"unicode\":\"🤳🏻\",\"name\":\"selfie: light skin tone\",\"variants\":[]},{\"unicode\":\"🤳🏼\",\"name\":\"selfie: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤳🏽\",\"name\":\"selfie: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤳🏾\",\"name\":\"selfie: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤳🏿\",\"name\":\"selfie: dark skin tone\",\"variants\":[]}]},\"💪\":{\"unicode\":\"💪\",\"name\":\"flexed biceps\",\"variants\":[{\"unicode\":\"💪🏻\",\"name\":\"flexed biceps: light skin tone\",\"variants\":[]},{\"unicode\":\"💪🏼\",\"name\":\"flexed biceps: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💪🏽\",\"name\":\"flexed biceps: medium skin tone\",\"variants\":[]},{\"unicode\":\"💪🏾\",\"name\":\"flexed biceps: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💪🏿\",\"name\":\"flexed biceps: dark skin tone\",\"variants\":[]}]},\"🦾\":{\"unicode\":\"🦾\",\"name\":\"mechanical arm\",\"variants\":[]},\"🦿\":{\"unicode\":\"🦿\",\"name\":\"mechanical leg\",\"variants\":[]},\"🦵\":{\"unicode\":\"🦵\",\"name\":\"leg\",\"variants\":[{\"unicode\":\"🦵🏻\",\"name\":\"leg: light skin tone\",\"variants\":[]},{\"unicode\":\"🦵🏼\",\"name\":\"leg: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🦵🏽\",\"name\":\"leg: medium skin tone\",\"variants\":[]},{\"unicode\":\"🦵🏾\",\"name\":\"leg: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🦵🏿\",\"name\":\"leg: dark skin tone\",\"variants\":[]}]},\"🦶\":{\"unicode\":\"🦶\",\"name\":\"foot\",\"variants\":[{\"unicode\":\"🦶🏻\",\"name\":\"foot: light skin tone\",\"variants\":[]},{\"unicode\":\"🦶🏼\",\"name\":\"foot: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🦶🏽\",\"name\":\"foot: medium skin tone\",\"variants\":[]},{\"unicode\":\"🦶🏾\",\"name\":\"foot: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🦶🏿\",\"name\":\"foot: dark skin tone\",\"variants\":[]}]},\"👂\":{\"unicode\":\"👂\",\"name\":\"ear\",\"variants\":[{\"unicode\":\"👂🏻\",\"name\":\"ear: light skin tone\",\"variants\":[]},{\"unicode\":\"👂🏼\",\"name\":\"ear: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👂🏽\",\"name\":\"ear: medium skin tone\",\"variants\":[]},{\"unicode\":\"👂🏾\",\"name\":\"ear: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👂🏿\",\"name\":\"ear: dark skin tone\",\"variants\":[]}]},\"🦻\":{\"unicode\":\"🦻\",\"name\":\"ear with hearing aid\",\"variants\":[{\"unicode\":\"🦻🏻\",\"name\":\"ear with hearing aid: light skin tone\",\"variants\":[]},{\"unicode\":\"🦻🏼\",\"name\":\"ear with hearing aid: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🦻🏽\",\"name\":\"ear with hearing aid: medium skin tone\",\"variants\":[]},{\"unicode\":\"🦻🏾\",\"name\":\"ear with hearing aid: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🦻🏿\",\"name\":\"ear with hearing aid: dark skin tone\",\"variants\":[]}]},\"👃\":{\"unicode\":\"👃\",\"name\":\"nose\",\"variants\":[{\"unicode\":\"👃🏻\",\"name\":\"nose: light skin tone\",\"variants\":[]},{\"unicode\":\"👃🏼\",\"name\":\"nose: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👃🏽\",\"name\":\"nose: medium skin tone\",\"variants\":[]},{\"unicode\":\"👃🏾\",\"name\":\"nose: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👃🏿\",\"name\":\"nose: dark skin tone\",\"variants\":[]}]},\"🧠\":{\"unicode\":\"🧠\",\"name\":\"brain\",\"variants\":[]},\"🫀\":{\"unicode\":\"🫀\",\"name\":\"anatomical heart\",\"variants\":[]},\"🫁\":{\"unicode\":\"🫁\",\"name\":\"lungs\",\"variants\":[]},\"🦷\":{\"unicode\":\"🦷\",\"name\":\"tooth\",\"variants\":[]},\"🦴\":{\"unicode\":\"🦴\",\"name\":\"bone\",\"variants\":[]},\"👀\":{\"unicode\":\"👀\",\"name\":\"eyes\",\"variants\":[]},\"👁️\":{\"unicode\":\"👁️\",\"name\":\"eye\",\"variants\":[]},\"👅\":{\"unicode\":\"👅\",\"name\":\"tongue\",\"variants\":[]},\"👄\":{\"unicode\":\"👄\",\"name\":\"mouth\",\"variants\":[]},\"👶\":{\"unicode\":\"👶\",\"name\":\"baby\",\"variants\":[{\"unicode\":\"👶🏻\",\"name\":\"baby: light skin tone\",\"variants\":[]},{\"unicode\":\"👶🏼\",\"name\":\"baby: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👶🏽\",\"name\":\"baby: medium skin tone\",\"variants\":[]},{\"unicode\":\"👶🏾\",\"name\":\"baby: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👶🏿\",\"name\":\"baby: dark skin tone\",\"variants\":[]}]},\"🧒\":{\"unicode\":\"🧒\",\"name\":\"child\",\"variants\":[{\"unicode\":\"🧒🏻\",\"name\":\"child: light skin tone\",\"variants\":[]},{\"unicode\":\"🧒🏼\",\"name\":\"child: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧒🏽\",\"name\":\"child: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧒🏾\",\"name\":\"child: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧒🏿\",\"name\":\"child: dark skin tone\",\"variants\":[]}]},\"👦\":{\"unicode\":\"👦\",\"name\":\"boy\",\"variants\":[{\"unicode\":\"👦🏻\",\"name\":\"boy: light skin tone\",\"variants\":[]},{\"unicode\":\"👦🏼\",\"name\":\"boy: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👦🏽\",\"name\":\"boy: medium skin tone\",\"variants\":[]},{\"unicode\":\"👦🏾\",\"name\":\"boy: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👦🏿\",\"name\":\"boy: dark skin tone\",\"variants\":[]}]},\"👧\":{\"unicode\":\"👧\",\"name\":\"girl\",\"variants\":[{\"unicode\":\"👧🏻\",\"name\":\"girl: light skin tone\",\"variants\":[]},{\"unicode\":\"👧🏼\",\"name\":\"girl: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👧🏽\",\"name\":\"girl: medium skin tone\",\"variants\":[]},{\"unicode\":\"👧🏾\",\"name\":\"girl: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👧🏿\",\"name\":\"girl: dark skin tone\",\"variants\":[]}]},\"🧑\":{\"unicode\":\"🧑\",\"name\":\"person\",\"variants\":[{\"unicode\":\"🧑🏻\",\"name\":\"person: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼\",\"name\":\"person: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽\",\"name\":\"person: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾\",\"name\":\"person: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿\",\"name\":\"person: dark skin tone\",\"variants\":[]}]},\"👱\":{\"unicode\":\"👱\",\"name\":\"person: blond hair\",\"variants\":[{\"unicode\":\"👱🏻\",\"name\":\"person: light skin tone, blond hair\",\"variants\":[]},{\"unicode\":\"👱🏼\",\"name\":\"person: medium-light skin tone, blond hair\",\"variants\":[]},{\"unicode\":\"👱🏽\",\"name\":\"person: medium skin tone, blond hair\",\"variants\":[]},{\"unicode\":\"👱🏾\",\"name\":\"person: medium-dark skin tone, blond hair\",\"variants\":[]},{\"unicode\":\"👱🏿\",\"name\":\"person: dark skin tone, blond hair\",\"variants\":[]}]},\"👨\":{\"unicode\":\"👨\",\"name\":\"man\",\"variants\":[{\"unicode\":\"👨🏻\",\"name\":\"man: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼\",\"name\":\"man: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽\",\"name\":\"man: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾\",\"name\":\"man: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿\",\"name\":\"man: dark skin tone\",\"variants\":[]}]},\"🧔\":{\"unicode\":\"🧔\",\"name\":\"man: beard\",\"variants\":[{\"unicode\":\"🧔🏻\",\"name\":\"man: light skin tone, beard\",\"variants\":[]},{\"unicode\":\"🧔🏼\",\"name\":\"man: medium-light skin tone, beard\",\"variants\":[]},{\"unicode\":\"🧔🏽\",\"name\":\"man: medium skin tone, beard\",\"variants\":[]},{\"unicode\":\"🧔🏾\",\"name\":\"man: medium-dark skin tone, beard\",\"variants\":[]},{\"unicode\":\"🧔🏿\",\"name\":\"man: dark skin tone, beard\",\"variants\":[]}]},\"👨‍🦰\":{\"unicode\":\"👨‍🦰\",\"name\":\"man: red hair\",\"variants\":[{\"unicode\":\"👨🏻‍🦰\",\"name\":\"man: light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦰\",\"name\":\"man: medium-light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦰\",\"name\":\"man: medium skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦰\",\"name\":\"man: medium-dark skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦰\",\"name\":\"man: dark skin tone, red hair\",\"variants\":[]}]},\"👨‍🦱\":{\"unicode\":\"👨‍🦱\",\"name\":\"man: curly hair\",\"variants\":[{\"unicode\":\"👨🏻‍🦱\",\"name\":\"man: light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦱\",\"name\":\"man: medium-light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦱\",\"name\":\"man: medium skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦱\",\"name\":\"man: medium-dark skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦱\",\"name\":\"man: dark skin tone, curly hair\",\"variants\":[]}]},\"👨‍🦳\":{\"unicode\":\"👨‍🦳\",\"name\":\"man: white hair\",\"variants\":[{\"unicode\":\"👨🏻‍🦳\",\"name\":\"man: light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦳\",\"name\":\"man: medium-light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦳\",\"name\":\"man: medium skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦳\",\"name\":\"man: medium-dark skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦳\",\"name\":\"man: dark skin tone, white hair\",\"variants\":[]}]},\"👨‍🦲\":{\"unicode\":\"👨‍🦲\",\"name\":\"man: bald\",\"variants\":[{\"unicode\":\"👨🏻‍🦲\",\"name\":\"man: light skin tone, bald\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦲\",\"name\":\"man: medium-light skin tone, bald\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦲\",\"name\":\"man: medium skin tone, bald\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦲\",\"name\":\"man: medium-dark skin tone, bald\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦲\",\"name\":\"man: dark skin tone, bald\",\"variants\":[]}]},\"👩\":{\"unicode\":\"👩\",\"name\":\"woman\",\"variants\":[{\"unicode\":\"👩🏻\",\"name\":\"woman: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼\",\"name\":\"woman: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽\",\"name\":\"woman: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾\",\"name\":\"woman: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿\",\"name\":\"woman: dark skin tone\",\"variants\":[]}]},\"👩‍🦰\":{\"unicode\":\"👩‍🦰\",\"name\":\"woman: red hair\",\"variants\":[{\"unicode\":\"👩🏻‍🦰\",\"name\":\"woman: light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦰\",\"name\":\"woman: medium-light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦰\",\"name\":\"woman: medium skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦰\",\"name\":\"woman: medium-dark skin tone, red hair\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦰\",\"name\":\"woman: dark skin tone, red hair\",\"variants\":[]}]},\"🧑‍🦰\":{\"unicode\":\"🧑‍🦰\",\"name\":\"person: red hair\",\"variants\":[{\"unicode\":\"🧑🏻‍🦰\",\"name\":\"person: light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦰\",\"name\":\"person: medium-light skin tone, red hair\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦰\",\"name\":\"person: medium skin tone, red hair\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦰\",\"name\":\"person: medium-dark skin tone, red hair\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦰\",\"name\":\"person: dark skin tone, red hair\",\"variants\":[]}]},\"👩‍🦱\":{\"unicode\":\"👩‍🦱\",\"name\":\"woman: curly hair\",\"variants\":[{\"unicode\":\"👩🏻‍🦱\",\"name\":\"woman: light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦱\",\"name\":\"woman: medium-light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦱\",\"name\":\"woman: medium skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦱\",\"name\":\"woman: medium-dark skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦱\",\"name\":\"woman: dark skin tone, curly hair\",\"variants\":[]}]},\"🧑‍🦱\":{\"unicode\":\"🧑‍🦱\",\"name\":\"person: curly hair\",\"variants\":[{\"unicode\":\"🧑🏻‍🦱\",\"name\":\"person: light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦱\",\"name\":\"person: medium-light skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦱\",\"name\":\"person: medium skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦱\",\"name\":\"person: medium-dark skin tone, curly hair\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦱\",\"name\":\"person: dark skin tone, curly hair\",\"variants\":[]}]},\"👩‍🦳\":{\"unicode\":\"👩‍🦳\",\"name\":\"woman: white hair\",\"variants\":[{\"unicode\":\"👩🏻‍🦳\",\"name\":\"woman: light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦳\",\"name\":\"woman: medium-light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦳\",\"name\":\"woman: medium skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦳\",\"name\":\"woman: medium-dark skin tone, white hair\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦳\",\"name\":\"woman: dark skin tone, white hair\",\"variants\":[]}]},\"🧑‍🦳\":{\"unicode\":\"🧑‍🦳\",\"name\":\"person: white hair\",\"variants\":[{\"unicode\":\"🧑🏻‍🦳\",\"name\":\"person: light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦳\",\"name\":\"person: medium-light skin tone, white hair\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦳\",\"name\":\"person: medium skin tone, white hair\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦳\",\"name\":\"person: medium-dark skin tone, white hair\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦳\",\"name\":\"person: dark skin tone, white hair\",\"variants\":[]}]},\"👩‍🦲\":{\"unicode\":\"👩‍🦲\",\"name\":\"woman: bald\",\"variants\":[{\"unicode\":\"👩🏻‍🦲\",\"name\":\"woman: light skin tone, bald\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦲\",\"name\":\"woman: medium-light skin tone, bald\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦲\",\"name\":\"woman: medium skin tone, bald\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦲\",\"name\":\"woman: medium-dark skin tone, bald\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦲\",\"name\":\"woman: dark skin tone, bald\",\"variants\":[]}]},\"🧑‍🦲\":{\"unicode\":\"🧑‍🦲\",\"name\":\"person: bald\",\"variants\":[{\"unicode\":\"🧑🏻‍🦲\",\"name\":\"person: light skin tone, bald\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦲\",\"name\":\"person: medium-light skin tone, bald\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦲\",\"name\":\"person: medium skin tone, bald\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦲\",\"name\":\"person: medium-dark skin tone, bald\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦲\",\"name\":\"person: dark skin tone, bald\",\"variants\":[]}]},\"👱‍♀️\":{\"unicode\":\"👱‍♀️\",\"name\":\"woman: blond hair\",\"variants\":[]},\"👱‍♂️\":{\"unicode\":\"👱‍♂️\",\"name\":\"man: blond hair\",\"variants\":[]},\"🧓\":{\"unicode\":\"🧓\",\"name\":\"older person\",\"variants\":[{\"unicode\":\"🧓🏻\",\"name\":\"older person: light skin tone\",\"variants\":[]},{\"unicode\":\"🧓🏼\",\"name\":\"older person: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧓🏽\",\"name\":\"older person: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧓🏾\",\"name\":\"older person: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧓🏿\",\"name\":\"older person: dark skin tone\",\"variants\":[]}]},\"👴\":{\"unicode\":\"👴\",\"name\":\"old man\",\"variants\":[{\"unicode\":\"👴🏻\",\"name\":\"old man: light skin tone\",\"variants\":[]},{\"unicode\":\"👴🏼\",\"name\":\"old man: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👴🏽\",\"name\":\"old man: medium skin tone\",\"variants\":[]},{\"unicode\":\"👴🏾\",\"name\":\"old man: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👴🏿\",\"name\":\"old man: dark skin tone\",\"variants\":[]}]},\"👵\":{\"unicode\":\"👵\",\"name\":\"old woman\",\"variants\":[{\"unicode\":\"👵🏻\",\"name\":\"old woman: light skin tone\",\"variants\":[]},{\"unicode\":\"👵🏼\",\"name\":\"old woman: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👵🏽\",\"name\":\"old woman: medium skin tone\",\"variants\":[]},{\"unicode\":\"👵🏾\",\"name\":\"old woman: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👵🏿\",\"name\":\"old woman: dark skin tone\",\"variants\":[]}]},\"🙍\":{\"unicode\":\"🙍\",\"name\":\"person frowning\",\"variants\":[{\"unicode\":\"🙍🏻\",\"name\":\"person frowning: light skin tone\",\"variants\":[]},{\"unicode\":\"🙍🏼\",\"name\":\"person frowning: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙍🏽\",\"name\":\"person frowning: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙍🏾\",\"name\":\"person frowning: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙍🏿\",\"name\":\"person frowning: dark skin tone\",\"variants\":[]}]},\"🙍‍♂️\":{\"unicode\":\"🙍‍♂️\",\"name\":\"man frowning\",\"variants\":[]},\"🙍‍♀️\":{\"unicode\":\"🙍‍♀️\",\"name\":\"woman frowning\",\"variants\":[]},\"🙎\":{\"unicode\":\"🙎\",\"name\":\"person pouting\",\"variants\":[{\"unicode\":\"🙎🏻\",\"name\":\"person pouting: light skin tone\",\"variants\":[]},{\"unicode\":\"🙎🏼\",\"name\":\"person pouting: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙎🏽\",\"name\":\"person pouting: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙎🏾\",\"name\":\"person pouting: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙎🏿\",\"name\":\"person pouting: dark skin tone\",\"variants\":[]}]},\"🙎‍♂️\":{\"unicode\":\"🙎‍♂️\",\"name\":\"man pouting\",\"variants\":[]},\"🙎‍♀️\":{\"unicode\":\"🙎‍♀️\",\"name\":\"woman pouting\",\"variants\":[]},\"🙅\":{\"unicode\":\"🙅\",\"name\":\"person gesturing NO\",\"variants\":[{\"unicode\":\"🙅🏻\",\"name\":\"person gesturing NO: light skin tone\",\"variants\":[]},{\"unicode\":\"🙅🏼\",\"name\":\"person gesturing NO: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙅🏽\",\"name\":\"person gesturing NO: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙅🏾\",\"name\":\"person gesturing NO: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙅🏿\",\"name\":\"person gesturing NO: dark skin tone\",\"variants\":[]}]},\"🙅‍♂️\":{\"unicode\":\"🙅‍♂️\",\"name\":\"man gesturing NO\",\"variants\":[]},\"🙅‍♀️\":{\"unicode\":\"🙅‍♀️\",\"name\":\"woman gesturing NO\",\"variants\":[]},\"🙆\":{\"unicode\":\"🙆\",\"name\":\"person gesturing OK\",\"variants\":[{\"unicode\":\"🙆🏻\",\"name\":\"person gesturing OK: light skin tone\",\"variants\":[]},{\"unicode\":\"🙆🏼\",\"name\":\"person gesturing OK: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙆🏽\",\"name\":\"person gesturing OK: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙆🏾\",\"name\":\"person gesturing OK: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙆🏿\",\"name\":\"person gesturing OK: dark skin tone\",\"variants\":[]}]},\"🙆‍♂️\":{\"unicode\":\"🙆‍♂️\",\"name\":\"man gesturing OK\",\"variants\":[]},\"🙆‍♀️\":{\"unicode\":\"🙆‍♀️\",\"name\":\"woman gesturing OK\",\"variants\":[]},\"💁\":{\"unicode\":\"💁\",\"name\":\"person tipping hand\",\"variants\":[{\"unicode\":\"💁🏻\",\"name\":\"person tipping hand: light skin tone\",\"variants\":[]},{\"unicode\":\"💁🏼\",\"name\":\"person tipping hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💁🏽\",\"name\":\"person tipping hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"💁🏾\",\"name\":\"person tipping hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💁🏿\",\"name\":\"person tipping hand: dark skin tone\",\"variants\":[]}]},\"💁‍♂️\":{\"unicode\":\"💁‍♂️\",\"name\":\"man tipping hand\",\"variants\":[]},\"💁‍♀️\":{\"unicode\":\"💁‍♀️\",\"name\":\"woman tipping hand\",\"variants\":[]},\"🙋\":{\"unicode\":\"🙋\",\"name\":\"person raising hand\",\"variants\":[{\"unicode\":\"🙋🏻\",\"name\":\"person raising hand: light skin tone\",\"variants\":[]},{\"unicode\":\"🙋🏼\",\"name\":\"person raising hand: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙋🏽\",\"name\":\"person raising hand: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙋🏾\",\"name\":\"person raising hand: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙋🏿\",\"name\":\"person raising hand: dark skin tone\",\"variants\":[]}]},\"🙋‍♂️\":{\"unicode\":\"🙋‍♂️\",\"name\":\"man raising hand\",\"variants\":[]},\"🙋‍♀️\":{\"unicode\":\"🙋‍♀️\",\"name\":\"woman raising hand\",\"variants\":[]},\"🧏\":{\"unicode\":\"🧏\",\"name\":\"deaf person\",\"variants\":[{\"unicode\":\"🧏🏻\",\"name\":\"deaf person: light skin tone\",\"variants\":[]},{\"unicode\":\"🧏🏼\",\"name\":\"deaf person: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧏🏽\",\"name\":\"deaf person: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧏🏾\",\"name\":\"deaf person: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧏🏿\",\"name\":\"deaf person: dark skin tone\",\"variants\":[]}]},\"🧏‍♂️\":{\"unicode\":\"🧏‍♂️\",\"name\":\"deaf man\",\"variants\":[]},\"🧏‍♀️\":{\"unicode\":\"🧏‍♀️\",\"name\":\"deaf woman\",\"variants\":[]},\"🙇\":{\"unicode\":\"🙇\",\"name\":\"person bowing\",\"variants\":[{\"unicode\":\"🙇🏻\",\"name\":\"person bowing: light skin tone\",\"variants\":[]},{\"unicode\":\"🙇🏼\",\"name\":\"person bowing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🙇🏽\",\"name\":\"person bowing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🙇🏾\",\"name\":\"person bowing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🙇🏿\",\"name\":\"person bowing: dark skin tone\",\"variants\":[]}]},\"🙇‍♂️\":{\"unicode\":\"🙇‍♂️\",\"name\":\"man bowing\",\"variants\":[]},\"🙇‍♀️\":{\"unicode\":\"🙇‍♀️\",\"name\":\"woman bowing\",\"variants\":[]},\"🤦\":{\"unicode\":\"🤦\",\"name\":\"person facepalming\",\"variants\":[{\"unicode\":\"🤦🏻\",\"name\":\"person facepalming: light skin tone\",\"variants\":[]},{\"unicode\":\"🤦🏼\",\"name\":\"person facepalming: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤦🏽\",\"name\":\"person facepalming: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤦🏾\",\"name\":\"person facepalming: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤦🏿\",\"name\":\"person facepalming: dark skin tone\",\"variants\":[]}]},\"🤦‍♂️\":{\"unicode\":\"🤦‍♂️\",\"name\":\"man facepalming\",\"variants\":[]},\"🤦‍♀️\":{\"unicode\":\"🤦‍♀️\",\"name\":\"woman facepalming\",\"variants\":[]},\"🤷\":{\"unicode\":\"🤷\",\"name\":\"person shrugging\",\"variants\":[{\"unicode\":\"🤷🏻\",\"name\":\"person shrugging: light skin tone\",\"variants\":[]},{\"unicode\":\"🤷🏼\",\"name\":\"person shrugging: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤷🏽\",\"name\":\"person shrugging: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤷🏾\",\"name\":\"person shrugging: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤷🏿\",\"name\":\"person shrugging: dark skin tone\",\"variants\":[]}]},\"🤷‍♂️\":{\"unicode\":\"🤷‍♂️\",\"name\":\"man shrugging\",\"variants\":[]},\"🤷‍♀️\":{\"unicode\":\"🤷‍♀️\",\"name\":\"woman shrugging\",\"variants\":[]},\"🧑‍⚕️\":{\"unicode\":\"🧑‍⚕️\",\"name\":\"health worker\",\"variants\":[]},\"👨‍⚕️\":{\"unicode\":\"👨‍⚕️\",\"name\":\"man health worker\",\"variants\":[]},\"👩‍⚕️\":{\"unicode\":\"👩‍⚕️\",\"name\":\"woman health worker\",\"variants\":[]},\"🧑‍🎓\":{\"unicode\":\"🧑‍🎓\",\"name\":\"student\",\"variants\":[{\"unicode\":\"🧑🏻‍🎓\",\"name\":\"student: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🎓\",\"name\":\"student: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🎓\",\"name\":\"student: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🎓\",\"name\":\"student: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🎓\",\"name\":\"student: dark skin tone\",\"variants\":[]}]},\"👨‍🎓\":{\"unicode\":\"👨‍🎓\",\"name\":\"man student\",\"variants\":[{\"unicode\":\"👨🏻‍🎓\",\"name\":\"man student: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🎓\",\"name\":\"man student: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🎓\",\"name\":\"man student: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🎓\",\"name\":\"man student: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🎓\",\"name\":\"man student: dark skin tone\",\"variants\":[]}]},\"👩‍🎓\":{\"unicode\":\"👩‍🎓\",\"name\":\"woman student\",\"variants\":[{\"unicode\":\"👩🏻‍🎓\",\"name\":\"woman student: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🎓\",\"name\":\"woman student: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🎓\",\"name\":\"woman student: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🎓\",\"name\":\"woman student: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🎓\",\"name\":\"woman student: dark skin tone\",\"variants\":[]}]},\"🧑‍🏫\":{\"unicode\":\"🧑‍🏫\",\"name\":\"teacher\",\"variants\":[{\"unicode\":\"🧑🏻‍🏫\",\"name\":\"teacher: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🏫\",\"name\":\"teacher: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🏫\",\"name\":\"teacher: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🏫\",\"name\":\"teacher: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🏫\",\"name\":\"teacher: dark skin tone\",\"variants\":[]}]},\"👨‍🏫\":{\"unicode\":\"👨‍🏫\",\"name\":\"man teacher\",\"variants\":[{\"unicode\":\"👨🏻‍🏫\",\"name\":\"man teacher: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🏫\",\"name\":\"man teacher: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🏫\",\"name\":\"man teacher: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🏫\",\"name\":\"man teacher: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🏫\",\"name\":\"man teacher: dark skin tone\",\"variants\":[]}]},\"👩‍🏫\":{\"unicode\":\"👩‍🏫\",\"name\":\"woman teacher\",\"variants\":[{\"unicode\":\"👩🏻‍🏫\",\"name\":\"woman teacher: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🏫\",\"name\":\"woman teacher: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🏫\",\"name\":\"woman teacher: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🏫\",\"name\":\"woman teacher: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🏫\",\"name\":\"woman teacher: dark skin tone\",\"variants\":[]}]},\"🧑‍⚖️\":{\"unicode\":\"🧑‍⚖️\",\"name\":\"judge\",\"variants\":[]},\"👨‍⚖️\":{\"unicode\":\"👨‍⚖️\",\"name\":\"man judge\",\"variants\":[]},\"👩‍⚖️\":{\"unicode\":\"👩‍⚖️\",\"name\":\"woman judge\",\"variants\":[]},\"🧑‍🌾\":{\"unicode\":\"🧑‍🌾\",\"name\":\"farmer\",\"variants\":[{\"unicode\":\"🧑🏻‍🌾\",\"name\":\"farmer: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🌾\",\"name\":\"farmer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🌾\",\"name\":\"farmer: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🌾\",\"name\":\"farmer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🌾\",\"name\":\"farmer: dark skin tone\",\"variants\":[]}]},\"👨‍🌾\":{\"unicode\":\"👨‍🌾\",\"name\":\"man farmer\",\"variants\":[{\"unicode\":\"👨🏻‍🌾\",\"name\":\"man farmer: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🌾\",\"name\":\"man farmer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🌾\",\"name\":\"man farmer: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🌾\",\"name\":\"man farmer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🌾\",\"name\":\"man farmer: dark skin tone\",\"variants\":[]}]},\"👩‍🌾\":{\"unicode\":\"👩‍🌾\",\"name\":\"woman farmer\",\"variants\":[{\"unicode\":\"👩🏻‍🌾\",\"name\":\"woman farmer: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🌾\",\"name\":\"woman farmer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🌾\",\"name\":\"woman farmer: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🌾\",\"name\":\"woman farmer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🌾\",\"name\":\"woman farmer: dark skin tone\",\"variants\":[]}]},\"🧑‍🍳\":{\"unicode\":\"🧑‍🍳\",\"name\":\"cook\",\"variants\":[{\"unicode\":\"🧑🏻‍🍳\",\"name\":\"cook: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🍳\",\"name\":\"cook: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🍳\",\"name\":\"cook: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🍳\",\"name\":\"cook: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🍳\",\"name\":\"cook: dark skin tone\",\"variants\":[]}]},\"👨‍🍳\":{\"unicode\":\"👨‍🍳\",\"name\":\"man cook\",\"variants\":[{\"unicode\":\"👨🏻‍🍳\",\"name\":\"man cook: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🍳\",\"name\":\"man cook: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🍳\",\"name\":\"man cook: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🍳\",\"name\":\"man cook: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🍳\",\"name\":\"man cook: dark skin tone\",\"variants\":[]}]},\"👩‍🍳\":{\"unicode\":\"👩‍🍳\",\"name\":\"woman cook\",\"variants\":[{\"unicode\":\"👩🏻‍🍳\",\"name\":\"woman cook: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🍳\",\"name\":\"woman cook: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🍳\",\"name\":\"woman cook: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🍳\",\"name\":\"woman cook: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🍳\",\"name\":\"woman cook: dark skin tone\",\"variants\":[]}]},\"🧑‍🔧\":{\"unicode\":\"🧑‍🔧\",\"name\":\"mechanic\",\"variants\":[{\"unicode\":\"🧑🏻‍🔧\",\"name\":\"mechanic: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🔧\",\"name\":\"mechanic: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🔧\",\"name\":\"mechanic: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🔧\",\"name\":\"mechanic: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🔧\",\"name\":\"mechanic: dark skin tone\",\"variants\":[]}]},\"👨‍🔧\":{\"unicode\":\"👨‍🔧\",\"name\":\"man mechanic\",\"variants\":[{\"unicode\":\"👨🏻‍🔧\",\"name\":\"man mechanic: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🔧\",\"name\":\"man mechanic: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🔧\",\"name\":\"man mechanic: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🔧\",\"name\":\"man mechanic: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🔧\",\"name\":\"man mechanic: dark skin tone\",\"variants\":[]}]},\"👩‍🔧\":{\"unicode\":\"👩‍🔧\",\"name\":\"woman mechanic\",\"variants\":[{\"unicode\":\"👩🏻‍🔧\",\"name\":\"woman mechanic: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🔧\",\"name\":\"woman mechanic: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🔧\",\"name\":\"woman mechanic: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🔧\",\"name\":\"woman mechanic: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🔧\",\"name\":\"woman mechanic: dark skin tone\",\"variants\":[]}]},\"🧑‍🏭\":{\"unicode\":\"🧑‍🏭\",\"name\":\"factory worker\",\"variants\":[{\"unicode\":\"🧑🏻‍🏭\",\"name\":\"factory worker: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🏭\",\"name\":\"factory worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🏭\",\"name\":\"factory worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🏭\",\"name\":\"factory worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🏭\",\"name\":\"factory worker: dark skin tone\",\"variants\":[]}]},\"👨‍🏭\":{\"unicode\":\"👨‍🏭\",\"name\":\"man factory worker\",\"variants\":[{\"unicode\":\"👨🏻‍🏭\",\"name\":\"man factory worker: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🏭\",\"name\":\"man factory worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🏭\",\"name\":\"man factory worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🏭\",\"name\":\"man factory worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🏭\",\"name\":\"man factory worker: dark skin tone\",\"variants\":[]}]},\"👩‍🏭\":{\"unicode\":\"👩‍🏭\",\"name\":\"woman factory worker\",\"variants\":[{\"unicode\":\"👩🏻‍🏭\",\"name\":\"woman factory worker: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🏭\",\"name\":\"woman factory worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🏭\",\"name\":\"woman factory worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🏭\",\"name\":\"woman factory worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🏭\",\"name\":\"woman factory worker: dark skin tone\",\"variants\":[]}]},\"🧑‍💼\":{\"unicode\":\"🧑‍💼\",\"name\":\"office worker\",\"variants\":[{\"unicode\":\"🧑🏻‍💼\",\"name\":\"office worker: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍💼\",\"name\":\"office worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍💼\",\"name\":\"office worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍💼\",\"name\":\"office worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍💼\",\"name\":\"office worker: dark skin tone\",\"variants\":[]}]},\"👨‍💼\":{\"unicode\":\"👨‍💼\",\"name\":\"man office worker\",\"variants\":[{\"unicode\":\"👨🏻‍💼\",\"name\":\"man office worker: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍💼\",\"name\":\"man office worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍💼\",\"name\":\"man office worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍💼\",\"name\":\"man office worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍💼\",\"name\":\"man office worker: dark skin tone\",\"variants\":[]}]},\"👩‍💼\":{\"unicode\":\"👩‍💼\",\"name\":\"woman office worker\",\"variants\":[{\"unicode\":\"👩🏻‍💼\",\"name\":\"woman office worker: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍💼\",\"name\":\"woman office worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍💼\",\"name\":\"woman office worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍💼\",\"name\":\"woman office worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍💼\",\"name\":\"woman office worker: dark skin tone\",\"variants\":[]}]},\"🧑‍🔬\":{\"unicode\":\"🧑‍🔬\",\"name\":\"scientist\",\"variants\":[{\"unicode\":\"🧑🏻‍🔬\",\"name\":\"scientist: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🔬\",\"name\":\"scientist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🔬\",\"name\":\"scientist: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🔬\",\"name\":\"scientist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🔬\",\"name\":\"scientist: dark skin tone\",\"variants\":[]}]},\"👨‍🔬\":{\"unicode\":\"👨‍🔬\",\"name\":\"man scientist\",\"variants\":[{\"unicode\":\"👨🏻‍🔬\",\"name\":\"man scientist: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🔬\",\"name\":\"man scientist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🔬\",\"name\":\"man scientist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🔬\",\"name\":\"man scientist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🔬\",\"name\":\"man scientist: dark skin tone\",\"variants\":[]}]},\"👩‍🔬\":{\"unicode\":\"👩‍🔬\",\"name\":\"woman scientist\",\"variants\":[{\"unicode\":\"👩🏻‍🔬\",\"name\":\"woman scientist: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🔬\",\"name\":\"woman scientist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🔬\",\"name\":\"woman scientist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🔬\",\"name\":\"woman scientist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🔬\",\"name\":\"woman scientist: dark skin tone\",\"variants\":[]}]},\"🧑‍💻\":{\"unicode\":\"🧑‍💻\",\"name\":\"technologist\",\"variants\":[{\"unicode\":\"🧑🏻‍💻\",\"name\":\"technologist: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍💻\",\"name\":\"technologist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍💻\",\"name\":\"technologist: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍💻\",\"name\":\"technologist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍💻\",\"name\":\"technologist: dark skin tone\",\"variants\":[]}]},\"👨‍💻\":{\"unicode\":\"👨‍💻\",\"name\":\"man technologist\",\"variants\":[{\"unicode\":\"👨🏻‍💻\",\"name\":\"man technologist: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍💻\",\"name\":\"man technologist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍💻\",\"name\":\"man technologist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍💻\",\"name\":\"man technologist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍💻\",\"name\":\"man technologist: dark skin tone\",\"variants\":[]}]},\"👩‍💻\":{\"unicode\":\"👩‍💻\",\"name\":\"woman technologist\",\"variants\":[{\"unicode\":\"👩🏻‍💻\",\"name\":\"woman technologist: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍💻\",\"name\":\"woman technologist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍💻\",\"name\":\"woman technologist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍💻\",\"name\":\"woman technologist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍💻\",\"name\":\"woman technologist: dark skin tone\",\"variants\":[]}]},\"🧑‍🎤\":{\"unicode\":\"🧑‍🎤\",\"name\":\"singer\",\"variants\":[{\"unicode\":\"🧑🏻‍🎤\",\"name\":\"singer: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🎤\",\"name\":\"singer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🎤\",\"name\":\"singer: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🎤\",\"name\":\"singer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🎤\",\"name\":\"singer: dark skin tone\",\"variants\":[]}]},\"👨‍🎤\":{\"unicode\":\"👨‍🎤\",\"name\":\"man singer\",\"variants\":[{\"unicode\":\"👨🏻‍🎤\",\"name\":\"man singer: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🎤\",\"name\":\"man singer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🎤\",\"name\":\"man singer: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🎤\",\"name\":\"man singer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🎤\",\"name\":\"man singer: dark skin tone\",\"variants\":[]}]},\"👩‍🎤\":{\"unicode\":\"👩‍🎤\",\"name\":\"woman singer\",\"variants\":[{\"unicode\":\"👩🏻‍🎤\",\"name\":\"woman singer: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🎤\",\"name\":\"woman singer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🎤\",\"name\":\"woman singer: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🎤\",\"name\":\"woman singer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🎤\",\"name\":\"woman singer: dark skin tone\",\"variants\":[]}]},\"🧑‍🎨\":{\"unicode\":\"🧑‍🎨\",\"name\":\"artist\",\"variants\":[{\"unicode\":\"🧑🏻‍🎨\",\"name\":\"artist: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🎨\",\"name\":\"artist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🎨\",\"name\":\"artist: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🎨\",\"name\":\"artist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🎨\",\"name\":\"artist: dark skin tone\",\"variants\":[]}]},\"👨‍🎨\":{\"unicode\":\"👨‍🎨\",\"name\":\"man artist\",\"variants\":[{\"unicode\":\"👨🏻‍🎨\",\"name\":\"man artist: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🎨\",\"name\":\"man artist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🎨\",\"name\":\"man artist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🎨\",\"name\":\"man artist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🎨\",\"name\":\"man artist: dark skin tone\",\"variants\":[]}]},\"👩‍🎨\":{\"unicode\":\"👩‍🎨\",\"name\":\"woman artist\",\"variants\":[{\"unicode\":\"👩🏻‍🎨\",\"name\":\"woman artist: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🎨\",\"name\":\"woman artist: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🎨\",\"name\":\"woman artist: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🎨\",\"name\":\"woman artist: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🎨\",\"name\":\"woman artist: dark skin tone\",\"variants\":[]}]},\"🧑‍✈️\":{\"unicode\":\"🧑‍✈️\",\"name\":\"pilot\",\"variants\":[]},\"👨‍✈️\":{\"unicode\":\"👨‍✈️\",\"name\":\"man pilot\",\"variants\":[]},\"👩‍✈️\":{\"unicode\":\"👩‍✈️\",\"name\":\"woman pilot\",\"variants\":[]},\"🧑‍🚀\":{\"unicode\":\"🧑‍🚀\",\"name\":\"astronaut\",\"variants\":[{\"unicode\":\"🧑🏻‍🚀\",\"name\":\"astronaut: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🚀\",\"name\":\"astronaut: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🚀\",\"name\":\"astronaut: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🚀\",\"name\":\"astronaut: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🚀\",\"name\":\"astronaut: dark skin tone\",\"variants\":[]}]},\"👨‍🚀\":{\"unicode\":\"👨‍🚀\",\"name\":\"man astronaut\",\"variants\":[{\"unicode\":\"👨🏻‍🚀\",\"name\":\"man astronaut: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🚀\",\"name\":\"man astronaut: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🚀\",\"name\":\"man astronaut: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🚀\",\"name\":\"man astronaut: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🚀\",\"name\":\"man astronaut: dark skin tone\",\"variants\":[]}]},\"👩‍🚀\":{\"unicode\":\"👩‍🚀\",\"name\":\"woman astronaut\",\"variants\":[{\"unicode\":\"👩🏻‍🚀\",\"name\":\"woman astronaut: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🚀\",\"name\":\"woman astronaut: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🚀\",\"name\":\"woman astronaut: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🚀\",\"name\":\"woman astronaut: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🚀\",\"name\":\"woman astronaut: dark skin tone\",\"variants\":[]}]},\"🧑‍🚒\":{\"unicode\":\"🧑‍🚒\",\"name\":\"firefighter\",\"variants\":[{\"unicode\":\"🧑🏻‍🚒\",\"name\":\"firefighter: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🚒\",\"name\":\"firefighter: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🚒\",\"name\":\"firefighter: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🚒\",\"name\":\"firefighter: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🚒\",\"name\":\"firefighter: dark skin tone\",\"variants\":[]}]},\"👨‍🚒\":{\"unicode\":\"👨‍🚒\",\"name\":\"man firefighter\",\"variants\":[{\"unicode\":\"👨🏻‍🚒\",\"name\":\"man firefighter: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🚒\",\"name\":\"man firefighter: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🚒\",\"name\":\"man firefighter: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🚒\",\"name\":\"man firefighter: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🚒\",\"name\":\"man firefighter: dark skin tone\",\"variants\":[]}]},\"👩‍🚒\":{\"unicode\":\"👩‍🚒\",\"name\":\"woman firefighter\",\"variants\":[{\"unicode\":\"👩🏻‍🚒\",\"name\":\"woman firefighter: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🚒\",\"name\":\"woman firefighter: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🚒\",\"name\":\"woman firefighter: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🚒\",\"name\":\"woman firefighter: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🚒\",\"name\":\"woman firefighter: dark skin tone\",\"variants\":[]}]},\"👮\":{\"unicode\":\"👮\",\"name\":\"police officer\",\"variants\":[{\"unicode\":\"👮🏻\",\"name\":\"police officer: light skin tone\",\"variants\":[]},{\"unicode\":\"👮🏼\",\"name\":\"police officer: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👮🏽\",\"name\":\"police officer: medium skin tone\",\"variants\":[]},{\"unicode\":\"👮🏾\",\"name\":\"police officer: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👮🏿\",\"name\":\"police officer: dark skin tone\",\"variants\":[]}]},\"👮‍♂️\":{\"unicode\":\"👮‍♂️\",\"name\":\"man police officer\",\"variants\":[]},\"👮‍♀️\":{\"unicode\":\"👮‍♀️\",\"name\":\"woman police officer\",\"variants\":[]},\"🕵️\":{\"unicode\":\"🕵️\",\"name\":\"detective\",\"variants\":[]},\"🕵️‍♂️\":{\"unicode\":\"🕵️‍♂️\",\"name\":\"man detective\",\"variants\":[]},\"🕵️‍♀️\":{\"unicode\":\"🕵️‍♀️\",\"name\":\"woman detective\",\"variants\":[]},\"💂\":{\"unicode\":\"💂\",\"name\":\"guard\",\"variants\":[{\"unicode\":\"💂🏻\",\"name\":\"guard: light skin tone\",\"variants\":[]},{\"unicode\":\"💂🏼\",\"name\":\"guard: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💂🏽\",\"name\":\"guard: medium skin tone\",\"variants\":[]},{\"unicode\":\"💂🏾\",\"name\":\"guard: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💂🏿\",\"name\":\"guard: dark skin tone\",\"variants\":[]}]},\"💂‍♂️\":{\"unicode\":\"💂‍♂️\",\"name\":\"man guard\",\"variants\":[]},\"💂‍♀️\":{\"unicode\":\"💂‍♀️\",\"name\":\"woman guard\",\"variants\":[]},\"🥷\":{\"unicode\":\"🥷\",\"name\":\"ninja\",\"variants\":[{\"unicode\":\"🥷🏻\",\"name\":\"ninja: light skin tone\",\"variants\":[]},{\"unicode\":\"🥷🏼\",\"name\":\"ninja: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🥷🏽\",\"name\":\"ninja: medium skin tone\",\"variants\":[]},{\"unicode\":\"🥷🏾\",\"name\":\"ninja: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🥷🏿\",\"name\":\"ninja: dark skin tone\",\"variants\":[]}]},\"👷\":{\"unicode\":\"👷\",\"name\":\"construction worker\",\"variants\":[{\"unicode\":\"👷🏻\",\"name\":\"construction worker: light skin tone\",\"variants\":[]},{\"unicode\":\"👷🏼\",\"name\":\"construction worker: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👷🏽\",\"name\":\"construction worker: medium skin tone\",\"variants\":[]},{\"unicode\":\"👷🏾\",\"name\":\"construction worker: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👷🏿\",\"name\":\"construction worker: dark skin tone\",\"variants\":[]}]},\"👷‍♂️\":{\"unicode\":\"👷‍♂️\",\"name\":\"man construction worker\",\"variants\":[]},\"👷‍♀️\":{\"unicode\":\"👷‍♀️\",\"name\":\"woman construction worker\",\"variants\":[]},\"🤴\":{\"unicode\":\"🤴\",\"name\":\"prince\",\"variants\":[{\"unicode\":\"🤴🏻\",\"name\":\"prince: light skin tone\",\"variants\":[]},{\"unicode\":\"🤴🏼\",\"name\":\"prince: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤴🏽\",\"name\":\"prince: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤴🏾\",\"name\":\"prince: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤴🏿\",\"name\":\"prince: dark skin tone\",\"variants\":[]}]},\"👸\":{\"unicode\":\"👸\",\"name\":\"princess\",\"variants\":[{\"unicode\":\"👸🏻\",\"name\":\"princess: light skin tone\",\"variants\":[]},{\"unicode\":\"👸🏼\",\"name\":\"princess: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👸🏽\",\"name\":\"princess: medium skin tone\",\"variants\":[]},{\"unicode\":\"👸🏾\",\"name\":\"princess: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👸🏿\",\"name\":\"princess: dark skin tone\",\"variants\":[]}]},\"👳\":{\"unicode\":\"👳\",\"name\":\"person wearing turban\",\"variants\":[{\"unicode\":\"👳🏻\",\"name\":\"person wearing turban: light skin tone\",\"variants\":[]},{\"unicode\":\"👳🏼\",\"name\":\"person wearing turban: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👳🏽\",\"name\":\"person wearing turban: medium skin tone\",\"variants\":[]},{\"unicode\":\"👳🏾\",\"name\":\"person wearing turban: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👳🏿\",\"name\":\"person wearing turban: dark skin tone\",\"variants\":[]}]},\"👳‍♂️\":{\"unicode\":\"👳‍♂️\",\"name\":\"man wearing turban\",\"variants\":[]},\"👳‍♀️\":{\"unicode\":\"👳‍♀️\",\"name\":\"woman wearing turban\",\"variants\":[]},\"👲\":{\"unicode\":\"👲\",\"name\":\"person with skullcap\",\"variants\":[{\"unicode\":\"👲🏻\",\"name\":\"person with skullcap: light skin tone\",\"variants\":[]},{\"unicode\":\"👲🏼\",\"name\":\"person with skullcap: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👲🏽\",\"name\":\"person with skullcap: medium skin tone\",\"variants\":[]},{\"unicode\":\"👲🏾\",\"name\":\"person with skullcap: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👲🏿\",\"name\":\"person with skullcap: dark skin tone\",\"variants\":[]}]},\"🧕\":{\"unicode\":\"🧕\",\"name\":\"woman with headscarf\",\"variants\":[{\"unicode\":\"🧕🏻\",\"name\":\"woman with headscarf: light skin tone\",\"variants\":[]},{\"unicode\":\"🧕🏼\",\"name\":\"woman with headscarf: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧕🏽\",\"name\":\"woman with headscarf: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧕🏾\",\"name\":\"woman with headscarf: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧕🏿\",\"name\":\"woman with headscarf: dark skin tone\",\"variants\":[]}]},\"🤵\":{\"unicode\":\"🤵\",\"name\":\"person in tuxedo\",\"variants\":[{\"unicode\":\"🤵🏻\",\"name\":\"person in tuxedo: light skin tone\",\"variants\":[]},{\"unicode\":\"🤵🏼\",\"name\":\"person in tuxedo: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤵🏽\",\"name\":\"person in tuxedo: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤵🏾\",\"name\":\"person in tuxedo: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤵🏿\",\"name\":\"person in tuxedo: dark skin tone\",\"variants\":[]}]},\"🤵‍♂️\":{\"unicode\":\"🤵‍♂️\",\"name\":\"man in tuxedo\",\"variants\":[]},\"🤵‍♀️\":{\"unicode\":\"🤵‍♀️\",\"name\":\"woman in tuxedo\",\"variants\":[]},\"👰\":{\"unicode\":\"👰\",\"name\":\"person with veil\",\"variants\":[{\"unicode\":\"👰🏻\",\"name\":\"person with veil: light skin tone\",\"variants\":[]},{\"unicode\":\"👰🏼\",\"name\":\"person with veil: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👰🏽\",\"name\":\"person with veil: medium skin tone\",\"variants\":[]},{\"unicode\":\"👰🏾\",\"name\":\"person with veil: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👰🏿\",\"name\":\"person with veil: dark skin tone\",\"variants\":[]}]},\"👰‍♂️\":{\"unicode\":\"👰‍♂️\",\"name\":\"man with veil\",\"variants\":[]},\"👰‍♀️\":{\"unicode\":\"👰‍♀️\",\"name\":\"woman with veil\",\"variants\":[]},\"🤰\":{\"unicode\":\"🤰\",\"name\":\"pregnant woman\",\"variants\":[{\"unicode\":\"🤰🏻\",\"name\":\"pregnant woman: light skin tone\",\"variants\":[]},{\"unicode\":\"🤰🏼\",\"name\":\"pregnant woman: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤰🏽\",\"name\":\"pregnant woman: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤰🏾\",\"name\":\"pregnant woman: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤰🏿\",\"name\":\"pregnant woman: dark skin tone\",\"variants\":[]}]},\"🤱\":{\"unicode\":\"🤱\",\"name\":\"breast-feeding\",\"variants\":[{\"unicode\":\"🤱🏻\",\"name\":\"breast-feeding: light skin tone\",\"variants\":[]},{\"unicode\":\"🤱🏼\",\"name\":\"breast-feeding: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤱🏽\",\"name\":\"breast-feeding: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤱🏾\",\"name\":\"breast-feeding: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤱🏿\",\"name\":\"breast-feeding: dark skin tone\",\"variants\":[]}]},\"👩‍🍼\":{\"unicode\":\"👩‍🍼\",\"name\":\"woman feeding baby\",\"variants\":[{\"unicode\":\"👩🏻‍🍼\",\"name\":\"woman feeding baby: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🍼\",\"name\":\"woman feeding baby: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🍼\",\"name\":\"woman feeding baby: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🍼\",\"name\":\"woman feeding baby: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🍼\",\"name\":\"woman feeding baby: dark skin tone\",\"variants\":[]}]},\"👨‍🍼\":{\"unicode\":\"👨‍🍼\",\"name\":\"man feeding baby\",\"variants\":[{\"unicode\":\"👨🏻‍🍼\",\"name\":\"man feeding baby: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🍼\",\"name\":\"man feeding baby: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🍼\",\"name\":\"man feeding baby: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🍼\",\"name\":\"man feeding baby: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🍼\",\"name\":\"man feeding baby: dark skin tone\",\"variants\":[]}]},\"🧑‍🍼\":{\"unicode\":\"🧑‍🍼\",\"name\":\"person feeding baby\",\"variants\":[{\"unicode\":\"🧑🏻‍🍼\",\"name\":\"person feeding baby: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🍼\",\"name\":\"person feeding baby: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🍼\",\"name\":\"person feeding baby: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🍼\",\"name\":\"person feeding baby: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🍼\",\"name\":\"person feeding baby: dark skin tone\",\"variants\":[]}]},\"👼\":{\"unicode\":\"👼\",\"name\":\"baby angel\",\"variants\":[{\"unicode\":\"👼🏻\",\"name\":\"baby angel: light skin tone\",\"variants\":[]},{\"unicode\":\"👼🏼\",\"name\":\"baby angel: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👼🏽\",\"name\":\"baby angel: medium skin tone\",\"variants\":[]},{\"unicode\":\"👼🏾\",\"name\":\"baby angel: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👼🏿\",\"name\":\"baby angel: dark skin tone\",\"variants\":[]}]},\"🎅\":{\"unicode\":\"🎅\",\"name\":\"Santa Claus\",\"variants\":[{\"unicode\":\"🎅🏻\",\"name\":\"Santa Claus: light skin tone\",\"variants\":[]},{\"unicode\":\"🎅🏼\",\"name\":\"Santa Claus: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🎅🏽\",\"name\":\"Santa Claus: medium skin tone\",\"variants\":[]},{\"unicode\":\"🎅🏾\",\"name\":\"Santa Claus: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🎅🏿\",\"name\":\"Santa Claus: dark skin tone\",\"variants\":[]}]},\"🤶\":{\"unicode\":\"🤶\",\"name\":\"Mrs. Claus\",\"variants\":[{\"unicode\":\"🤶🏻\",\"name\":\"Mrs. Claus: light skin tone\",\"variants\":[]},{\"unicode\":\"🤶🏼\",\"name\":\"Mrs. Claus: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤶🏽\",\"name\":\"Mrs. Claus: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤶🏾\",\"name\":\"Mrs. Claus: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤶🏿\",\"name\":\"Mrs. Claus: dark skin tone\",\"variants\":[]}]},\"🧑‍🎄\":{\"unicode\":\"🧑‍🎄\",\"name\":\"mx claus\",\"variants\":[{\"unicode\":\"🧑🏻‍🎄\",\"name\":\"mx claus: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🎄\",\"name\":\"mx claus: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🎄\",\"name\":\"mx claus: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🎄\",\"name\":\"mx claus: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🎄\",\"name\":\"mx claus: dark skin tone\",\"variants\":[]}]},\"🦸\":{\"unicode\":\"🦸\",\"name\":\"superhero\",\"variants\":[{\"unicode\":\"🦸🏻\",\"name\":\"superhero: light skin tone\",\"variants\":[]},{\"unicode\":\"🦸🏼\",\"name\":\"superhero: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🦸🏽\",\"name\":\"superhero: medium skin tone\",\"variants\":[]},{\"unicode\":\"🦸🏾\",\"name\":\"superhero: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🦸🏿\",\"name\":\"superhero: dark skin tone\",\"variants\":[]}]},\"🦸‍♂️\":{\"unicode\":\"🦸‍♂️\",\"name\":\"man superhero\",\"variants\":[]},\"🦸‍♀️\":{\"unicode\":\"🦸‍♀️\",\"name\":\"woman superhero\",\"variants\":[]},\"🦹\":{\"unicode\":\"🦹\",\"name\":\"supervillain\",\"variants\":[{\"unicode\":\"🦹🏻\",\"name\":\"supervillain: light skin tone\",\"variants\":[]},{\"unicode\":\"🦹🏼\",\"name\":\"supervillain: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🦹🏽\",\"name\":\"supervillain: medium skin tone\",\"variants\":[]},{\"unicode\":\"🦹🏾\",\"name\":\"supervillain: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🦹🏿\",\"name\":\"supervillain: dark skin tone\",\"variants\":[]}]},\"🦹‍♂️\":{\"unicode\":\"🦹‍♂️\",\"name\":\"man supervillain\",\"variants\":[]},\"🦹‍♀️\":{\"unicode\":\"🦹‍♀️\",\"name\":\"woman supervillain\",\"variants\":[]},\"🧙\":{\"unicode\":\"🧙\",\"name\":\"mage\",\"variants\":[{\"unicode\":\"🧙🏻\",\"name\":\"mage: light skin tone\",\"variants\":[]},{\"unicode\":\"🧙🏼\",\"name\":\"mage: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧙🏽\",\"name\":\"mage: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧙🏾\",\"name\":\"mage: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧙🏿\",\"name\":\"mage: dark skin tone\",\"variants\":[]}]},\"🧙‍♂️\":{\"unicode\":\"🧙‍♂️\",\"name\":\"man mage\",\"variants\":[]},\"🧙‍♀️\":{\"unicode\":\"🧙‍♀️\",\"name\":\"woman mage\",\"variants\":[]},\"🧚\":{\"unicode\":\"🧚\",\"name\":\"fairy\",\"variants\":[{\"unicode\":\"🧚🏻\",\"name\":\"fairy: light skin tone\",\"variants\":[]},{\"unicode\":\"🧚🏼\",\"name\":\"fairy: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧚🏽\",\"name\":\"fairy: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧚🏾\",\"name\":\"fairy: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧚🏿\",\"name\":\"fairy: dark skin tone\",\"variants\":[]}]},\"🧚‍♂️\":{\"unicode\":\"🧚‍♂️\",\"name\":\"man fairy\",\"variants\":[]},\"🧚‍♀️\":{\"unicode\":\"🧚‍♀️\",\"name\":\"woman fairy\",\"variants\":[]},\"🧛\":{\"unicode\":\"🧛\",\"name\":\"vampire\",\"variants\":[{\"unicode\":\"🧛🏻\",\"name\":\"vampire: light skin tone\",\"variants\":[]},{\"unicode\":\"🧛🏼\",\"name\":\"vampire: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧛🏽\",\"name\":\"vampire: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧛🏾\",\"name\":\"vampire: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧛🏿\",\"name\":\"vampire: dark skin tone\",\"variants\":[]}]},\"🧛‍♂️\":{\"unicode\":\"🧛‍♂️\",\"name\":\"man vampire\",\"variants\":[]},\"🧛‍♀️\":{\"unicode\":\"🧛‍♀️\",\"name\":\"woman vampire\",\"variants\":[]},\"🧜\":{\"unicode\":\"🧜\",\"name\":\"merperson\",\"variants\":[{\"unicode\":\"🧜🏻\",\"name\":\"merperson: light skin tone\",\"variants\":[]},{\"unicode\":\"🧜🏼\",\"name\":\"merperson: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧜🏽\",\"name\":\"merperson: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧜🏾\",\"name\":\"merperson: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧜🏿\",\"name\":\"merperson: dark skin tone\",\"variants\":[]}]},\"🧜‍♂️\":{\"unicode\":\"🧜‍♂️\",\"name\":\"merman\",\"variants\":[]},\"🧜‍♀️\":{\"unicode\":\"🧜‍♀️\",\"name\":\"mermaid\",\"variants\":[]},\"🧝\":{\"unicode\":\"🧝\",\"name\":\"elf\",\"variants\":[{\"unicode\":\"🧝🏻\",\"name\":\"elf: light skin tone\",\"variants\":[]},{\"unicode\":\"🧝🏼\",\"name\":\"elf: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧝🏽\",\"name\":\"elf: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧝🏾\",\"name\":\"elf: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧝🏿\",\"name\":\"elf: dark skin tone\",\"variants\":[]}]},\"🧝‍♂️\":{\"unicode\":\"🧝‍♂️\",\"name\":\"man elf\",\"variants\":[]},\"🧝‍♀️\":{\"unicode\":\"🧝‍♀️\",\"name\":\"woman elf\",\"variants\":[]},\"🧞\":{\"unicode\":\"🧞\",\"name\":\"genie\",\"variants\":[]},\"🧞‍♂️\":{\"unicode\":\"🧞‍♂️\",\"name\":\"man genie\",\"variants\":[]},\"🧞‍♀️\":{\"unicode\":\"🧞‍♀️\",\"name\":\"woman genie\",\"variants\":[]},\"🧟\":{\"unicode\":\"🧟\",\"name\":\"zombie\",\"variants\":[]},\"🧟‍♂️\":{\"unicode\":\"🧟‍♂️\",\"name\":\"man zombie\",\"variants\":[]},\"🧟‍♀️\":{\"unicode\":\"🧟‍♀️\",\"name\":\"woman zombie\",\"variants\":[]},\"💆\":{\"unicode\":\"💆\",\"name\":\"person getting massage\",\"variants\":[{\"unicode\":\"💆🏻\",\"name\":\"person getting massage: light skin tone\",\"variants\":[]},{\"unicode\":\"💆🏼\",\"name\":\"person getting massage: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💆🏽\",\"name\":\"person getting massage: medium skin tone\",\"variants\":[]},{\"unicode\":\"💆🏾\",\"name\":\"person getting massage: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💆🏿\",\"name\":\"person getting massage: dark skin tone\",\"variants\":[]}]},\"💆‍♂️\":{\"unicode\":\"💆‍♂️\",\"name\":\"man getting massage\",\"variants\":[]},\"💆‍♀️\":{\"unicode\":\"💆‍♀️\",\"name\":\"woman getting massage\",\"variants\":[]},\"💇\":{\"unicode\":\"💇\",\"name\":\"person getting haircut\",\"variants\":[{\"unicode\":\"💇🏻\",\"name\":\"person getting haircut: light skin tone\",\"variants\":[]},{\"unicode\":\"💇🏼\",\"name\":\"person getting haircut: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💇🏽\",\"name\":\"person getting haircut: medium skin tone\",\"variants\":[]},{\"unicode\":\"💇🏾\",\"name\":\"person getting haircut: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💇🏿\",\"name\":\"person getting haircut: dark skin tone\",\"variants\":[]}]},\"💇‍♂️\":{\"unicode\":\"💇‍♂️\",\"name\":\"man getting haircut\",\"variants\":[]},\"💇‍♀️\":{\"unicode\":\"💇‍♀️\",\"name\":\"woman getting haircut\",\"variants\":[]},\"🚶\":{\"unicode\":\"🚶\",\"name\":\"person walking\",\"variants\":[{\"unicode\":\"🚶🏻\",\"name\":\"person walking: light skin tone\",\"variants\":[]},{\"unicode\":\"🚶🏼\",\"name\":\"person walking: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🚶🏽\",\"name\":\"person walking: medium skin tone\",\"variants\":[]},{\"unicode\":\"🚶🏾\",\"name\":\"person walking: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🚶🏿\",\"name\":\"person walking: dark skin tone\",\"variants\":[]}]},\"🚶‍♂️\":{\"unicode\":\"🚶‍♂️\",\"name\":\"man walking\",\"variants\":[]},\"🚶‍♀️\":{\"unicode\":\"🚶‍♀️\",\"name\":\"woman walking\",\"variants\":[]},\"🧍\":{\"unicode\":\"🧍\",\"name\":\"person standing\",\"variants\":[{\"unicode\":\"🧍🏻\",\"name\":\"person standing: light skin tone\",\"variants\":[]},{\"unicode\":\"🧍🏼\",\"name\":\"person standing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧍🏽\",\"name\":\"person standing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧍🏾\",\"name\":\"person standing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧍🏿\",\"name\":\"person standing: dark skin tone\",\"variants\":[]}]},\"🧍‍♂️\":{\"unicode\":\"🧍‍♂️\",\"name\":\"man standing\",\"variants\":[]},\"🧍‍♀️\":{\"unicode\":\"🧍‍♀️\",\"name\":\"woman standing\",\"variants\":[]},\"🧎\":{\"unicode\":\"🧎\",\"name\":\"person kneeling\",\"variants\":[{\"unicode\":\"🧎🏻\",\"name\":\"person kneeling: light skin tone\",\"variants\":[]},{\"unicode\":\"🧎🏼\",\"name\":\"person kneeling: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧎🏽\",\"name\":\"person kneeling: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧎🏾\",\"name\":\"person kneeling: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧎🏿\",\"name\":\"person kneeling: dark skin tone\",\"variants\":[]}]},\"🧎‍♂️\":{\"unicode\":\"🧎‍♂️\",\"name\":\"man kneeling\",\"variants\":[]},\"🧎‍♀️\":{\"unicode\":\"🧎‍♀️\",\"name\":\"woman kneeling\",\"variants\":[]},\"🧑‍🦯\":{\"unicode\":\"🧑‍🦯\",\"name\":\"person with white cane\",\"variants\":[{\"unicode\":\"🧑🏻‍🦯\",\"name\":\"person with white cane: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦯\",\"name\":\"person with white cane: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦯\",\"name\":\"person with white cane: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦯\",\"name\":\"person with white cane: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦯\",\"name\":\"person with white cane: dark skin tone\",\"variants\":[]}]},\"👨‍🦯\":{\"unicode\":\"👨‍🦯\",\"name\":\"man with white cane\",\"variants\":[{\"unicode\":\"👨🏻‍🦯\",\"name\":\"man with white cane: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦯\",\"name\":\"man with white cane: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦯\",\"name\":\"man with white cane: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦯\",\"name\":\"man with white cane: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦯\",\"name\":\"man with white cane: dark skin tone\",\"variants\":[]}]},\"👩‍🦯\":{\"unicode\":\"👩‍🦯\",\"name\":\"woman with white cane\",\"variants\":[{\"unicode\":\"👩🏻‍🦯\",\"name\":\"woman with white cane: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦯\",\"name\":\"woman with white cane: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦯\",\"name\":\"woman with white cane: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦯\",\"name\":\"woman with white cane: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦯\",\"name\":\"woman with white cane: dark skin tone\",\"variants\":[]}]},\"🧑‍🦼\":{\"unicode\":\"🧑‍🦼\",\"name\":\"person in motorized wheelchair\",\"variants\":[{\"unicode\":\"🧑🏻‍🦼\",\"name\":\"person in motorized wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦼\",\"name\":\"person in motorized wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦼\",\"name\":\"person in motorized wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦼\",\"name\":\"person in motorized wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦼\",\"name\":\"person in motorized wheelchair: dark skin tone\",\"variants\":[]}]},\"👨‍🦼\":{\"unicode\":\"👨‍🦼\",\"name\":\"man in motorized wheelchair\",\"variants\":[{\"unicode\":\"👨🏻‍🦼\",\"name\":\"man in motorized wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦼\",\"name\":\"man in motorized wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦼\",\"name\":\"man in motorized wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦼\",\"name\":\"man in motorized wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦼\",\"name\":\"man in motorized wheelchair: dark skin tone\",\"variants\":[]}]},\"👩‍🦼\":{\"unicode\":\"👩‍🦼\",\"name\":\"woman in motorized wheelchair\",\"variants\":[{\"unicode\":\"👩🏻‍🦼\",\"name\":\"woman in motorized wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦼\",\"name\":\"woman in motorized wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦼\",\"name\":\"woman in motorized wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦼\",\"name\":\"woman in motorized wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦼\",\"name\":\"woman in motorized wheelchair: dark skin tone\",\"variants\":[]}]},\"🧑‍🦽\":{\"unicode\":\"🧑‍🦽\",\"name\":\"person in manual wheelchair\",\"variants\":[{\"unicode\":\"🧑🏻‍🦽\",\"name\":\"person in manual wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🦽\",\"name\":\"person in manual wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🦽\",\"name\":\"person in manual wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🦽\",\"name\":\"person in manual wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🦽\",\"name\":\"person in manual wheelchair: dark skin tone\",\"variants\":[]}]},\"👨‍🦽\":{\"unicode\":\"👨‍🦽\",\"name\":\"man in manual wheelchair\",\"variants\":[{\"unicode\":\"👨🏻‍🦽\",\"name\":\"man in manual wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏼‍🦽\",\"name\":\"man in manual wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👨🏽‍🦽\",\"name\":\"man in manual wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"👨🏾‍🦽\",\"name\":\"man in manual wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👨🏿‍🦽\",\"name\":\"man in manual wheelchair: dark skin tone\",\"variants\":[]}]},\"👩‍🦽\":{\"unicode\":\"👩‍🦽\",\"name\":\"woman in manual wheelchair\",\"variants\":[{\"unicode\":\"👩🏻‍🦽\",\"name\":\"woman in manual wheelchair: light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏼‍🦽\",\"name\":\"woman in manual wheelchair: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👩🏽‍🦽\",\"name\":\"woman in manual wheelchair: medium skin tone\",\"variants\":[]},{\"unicode\":\"👩🏾‍🦽\",\"name\":\"woman in manual wheelchair: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👩🏿‍🦽\",\"name\":\"woman in manual wheelchair: dark skin tone\",\"variants\":[]}]},\"🏃\":{\"unicode\":\"🏃\",\"name\":\"person running\",\"variants\":[{\"unicode\":\"🏃🏻\",\"name\":\"person running: light skin tone\",\"variants\":[]},{\"unicode\":\"🏃🏼\",\"name\":\"person running: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🏃🏽\",\"name\":\"person running: medium skin tone\",\"variants\":[]},{\"unicode\":\"🏃🏾\",\"name\":\"person running: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🏃🏿\",\"name\":\"person running: dark skin tone\",\"variants\":[]}]},\"🏃‍♂️\":{\"unicode\":\"🏃‍♂️\",\"name\":\"man running\",\"variants\":[]},\"🏃‍♀️\":{\"unicode\":\"🏃‍♀️\",\"name\":\"woman running\",\"variants\":[]},\"💃\":{\"unicode\":\"💃\",\"name\":\"woman dancing\",\"variants\":[{\"unicode\":\"💃🏻\",\"name\":\"woman dancing: light skin tone\",\"variants\":[]},{\"unicode\":\"💃🏼\",\"name\":\"woman dancing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"💃🏽\",\"name\":\"woman dancing: medium skin tone\",\"variants\":[]},{\"unicode\":\"💃🏾\",\"name\":\"woman dancing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"💃🏿\",\"name\":\"woman dancing: dark skin tone\",\"variants\":[]}]},\"🕺\":{\"unicode\":\"🕺\",\"name\":\"man dancing\",\"variants\":[{\"unicode\":\"🕺🏻\",\"name\":\"man dancing: light skin tone\",\"variants\":[]},{\"unicode\":\"🕺🏼\",\"name\":\"man dancing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🕺🏽\",\"name\":\"man dancing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🕺🏾\",\"name\":\"man dancing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🕺🏿\",\"name\":\"man dancing: dark skin tone\",\"variants\":[]}]},\"🕴️\":{\"unicode\":\"🕴️\",\"name\":\"person in suit levitating\",\"variants\":[]},\"👯\":{\"unicode\":\"👯\",\"name\":\"people with bunny ears\",\"variants\":[]},\"👯‍♂️\":{\"unicode\":\"👯‍♂️\",\"name\":\"men with bunny ears\",\"variants\":[]},\"👯‍♀️\":{\"unicode\":\"👯‍♀️\",\"name\":\"women with bunny ears\",\"variants\":[]},\"🧖\":{\"unicode\":\"🧖\",\"name\":\"person in steamy room\",\"variants\":[{\"unicode\":\"🧖🏻\",\"name\":\"person in steamy room: light skin tone\",\"variants\":[]},{\"unicode\":\"🧖🏼\",\"name\":\"person in steamy room: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧖🏽\",\"name\":\"person in steamy room: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧖🏾\",\"name\":\"person in steamy room: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧖🏿\",\"name\":\"person in steamy room: dark skin tone\",\"variants\":[]}]},\"🧖‍♂️\":{\"unicode\":\"🧖‍♂️\",\"name\":\"man in steamy room\",\"variants\":[]},\"🧖‍♀️\":{\"unicode\":\"🧖‍♀️\",\"name\":\"woman in steamy room\",\"variants\":[]},\"🧗\":{\"unicode\":\"🧗\",\"name\":\"person climbing\",\"variants\":[{\"unicode\":\"🧗🏻\",\"name\":\"person climbing: light skin tone\",\"variants\":[]},{\"unicode\":\"🧗🏼\",\"name\":\"person climbing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧗🏽\",\"name\":\"person climbing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧗🏾\",\"name\":\"person climbing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧗🏿\",\"name\":\"person climbing: dark skin tone\",\"variants\":[]}]},\"🧗‍♂️\":{\"unicode\":\"🧗‍♂️\",\"name\":\"man climbing\",\"variants\":[]},\"🧗‍♀️\":{\"unicode\":\"🧗‍♀️\",\"name\":\"woman climbing\",\"variants\":[]},\"🤺\":{\"unicode\":\"🤺\",\"name\":\"person fencing\",\"variants\":[]},\"🏇\":{\"unicode\":\"🏇\",\"name\":\"horse racing\",\"variants\":[{\"unicode\":\"🏇🏻\",\"name\":\"horse racing: light skin tone\",\"variants\":[]},{\"unicode\":\"🏇🏼\",\"name\":\"horse racing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🏇🏽\",\"name\":\"horse racing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🏇🏾\",\"name\":\"horse racing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🏇🏿\",\"name\":\"horse racing: dark skin tone\",\"variants\":[]}]},\"⛷️\":{\"unicode\":\"⛷️\",\"name\":\"skier\",\"variants\":[]},\"🏂\":{\"unicode\":\"🏂\",\"name\":\"snowboarder\",\"variants\":[{\"unicode\":\"🏂🏻\",\"name\":\"snowboarder: light skin tone\",\"variants\":[]},{\"unicode\":\"🏂🏼\",\"name\":\"snowboarder: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🏂🏽\",\"name\":\"snowboarder: medium skin tone\",\"variants\":[]},{\"unicode\":\"🏂🏾\",\"name\":\"snowboarder: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🏂🏿\",\"name\":\"snowboarder: dark skin tone\",\"variants\":[]}]},\"🏌️\":{\"unicode\":\"🏌️\",\"name\":\"person golfing\",\"variants\":[]},\"🏌️‍♂️\":{\"unicode\":\"🏌️‍♂️\",\"name\":\"man golfing\",\"variants\":[]},\"🏌️‍♀️\":{\"unicode\":\"🏌️‍♀️\",\"name\":\"woman golfing\",\"variants\":[]},\"🏄\":{\"unicode\":\"🏄\",\"name\":\"person surfing\",\"variants\":[{\"unicode\":\"🏄🏻\",\"name\":\"person surfing: light skin tone\",\"variants\":[]},{\"unicode\":\"🏄🏼\",\"name\":\"person surfing: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🏄🏽\",\"name\":\"person surfing: medium skin tone\",\"variants\":[]},{\"unicode\":\"🏄🏾\",\"name\":\"person surfing: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🏄🏿\",\"name\":\"person surfing: dark skin tone\",\"variants\":[]}]},\"🏄‍♂️\":{\"unicode\":\"🏄‍♂️\",\"name\":\"man surfing\",\"variants\":[]},\"🏄‍♀️\":{\"unicode\":\"🏄‍♀️\",\"name\":\"woman surfing\",\"variants\":[]},\"🚣\":{\"unicode\":\"🚣\",\"name\":\"person rowing boat\",\"variants\":[{\"unicode\":\"🚣🏻\",\"name\":\"person rowing boat: light skin tone\",\"variants\":[]},{\"unicode\":\"🚣🏼\",\"name\":\"person rowing boat: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🚣🏽\",\"name\":\"person rowing boat: medium skin tone\",\"variants\":[]},{\"unicode\":\"🚣🏾\",\"name\":\"person rowing boat: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🚣🏿\",\"name\":\"person rowing boat: dark skin tone\",\"variants\":[]}]},\"🚣‍♂️\":{\"unicode\":\"🚣‍♂️\",\"name\":\"man rowing boat\",\"variants\":[]},\"🚣‍♀️\":{\"unicode\":\"🚣‍♀️\",\"name\":\"woman rowing boat\",\"variants\":[]},\"🏊\":{\"unicode\":\"🏊\",\"name\":\"person swimming\",\"variants\":[{\"unicode\":\"🏊🏻\",\"name\":\"person swimming: light skin tone\",\"variants\":[]},{\"unicode\":\"🏊🏼\",\"name\":\"person swimming: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🏊🏽\",\"name\":\"person swimming: medium skin tone\",\"variants\":[]},{\"unicode\":\"🏊🏾\",\"name\":\"person swimming: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🏊🏿\",\"name\":\"person swimming: dark skin tone\",\"variants\":[]}]},\"🏊‍♂️\":{\"unicode\":\"🏊‍♂️\",\"name\":\"man swimming\",\"variants\":[]},\"🏊‍♀️\":{\"unicode\":\"🏊‍♀️\",\"name\":\"woman swimming\",\"variants\":[]},\"⛹️\":{\"unicode\":\"⛹️\",\"name\":\"person bouncing ball\",\"variants\":[]},\"⛹️‍♂️\":{\"unicode\":\"⛹️‍♂️\",\"name\":\"man bouncing ball\",\"variants\":[]},\"⛹️‍♀️\":{\"unicode\":\"⛹️‍♀️\",\"name\":\"woman bouncing ball\",\"variants\":[]},\"🏋️\":{\"unicode\":\"🏋️\",\"name\":\"person lifting weights\",\"variants\":[]},\"🏋️‍♂️\":{\"unicode\":\"🏋️‍♂️\",\"name\":\"man lifting weights\",\"variants\":[]},\"🏋️‍♀️\":{\"unicode\":\"🏋️‍♀️\",\"name\":\"woman lifting weights\",\"variants\":[]},\"🚴\":{\"unicode\":\"🚴\",\"name\":\"person biking\",\"variants\":[{\"unicode\":\"🚴🏻\",\"name\":\"person biking: light skin tone\",\"variants\":[]},{\"unicode\":\"🚴🏼\",\"name\":\"person biking: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🚴🏽\",\"name\":\"person biking: medium skin tone\",\"variants\":[]},{\"unicode\":\"🚴🏾\",\"name\":\"person biking: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🚴🏿\",\"name\":\"person biking: dark skin tone\",\"variants\":[]}]},\"🚴‍♂️\":{\"unicode\":\"🚴‍♂️\",\"name\":\"man biking\",\"variants\":[]},\"🚴‍♀️\":{\"unicode\":\"🚴‍♀️\",\"name\":\"woman biking\",\"variants\":[]},\"🚵\":{\"unicode\":\"🚵\",\"name\":\"person mountain biking\",\"variants\":[{\"unicode\":\"🚵🏻\",\"name\":\"person mountain biking: light skin tone\",\"variants\":[]},{\"unicode\":\"🚵🏼\",\"name\":\"person mountain biking: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🚵🏽\",\"name\":\"person mountain biking: medium skin tone\",\"variants\":[]},{\"unicode\":\"🚵🏾\",\"name\":\"person mountain biking: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🚵🏿\",\"name\":\"person mountain biking: dark skin tone\",\"variants\":[]}]},\"🚵‍♂️\":{\"unicode\":\"🚵‍♂️\",\"name\":\"man mountain biking\",\"variants\":[]},\"🚵‍♀️\":{\"unicode\":\"🚵‍♀️\",\"name\":\"woman mountain biking\",\"variants\":[]},\"🤸\":{\"unicode\":\"🤸\",\"name\":\"person cartwheeling\",\"variants\":[{\"unicode\":\"🤸🏻\",\"name\":\"person cartwheeling: light skin tone\",\"variants\":[]},{\"unicode\":\"🤸🏼\",\"name\":\"person cartwheeling: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤸🏽\",\"name\":\"person cartwheeling: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤸🏾\",\"name\":\"person cartwheeling: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤸🏿\",\"name\":\"person cartwheeling: dark skin tone\",\"variants\":[]}]},\"🤸‍♂️\":{\"unicode\":\"🤸‍♂️\",\"name\":\"man cartwheeling\",\"variants\":[]},\"🤸‍♀️\":{\"unicode\":\"🤸‍♀️\",\"name\":\"woman cartwheeling\",\"variants\":[]},\"🤼\":{\"unicode\":\"🤼\",\"name\":\"people wrestling\",\"variants\":[]},\"🤼‍♂️\":{\"unicode\":\"🤼‍♂️\",\"name\":\"men wrestling\",\"variants\":[]},\"🤼‍♀️\":{\"unicode\":\"🤼‍♀️\",\"name\":\"women wrestling\",\"variants\":[]},\"🤽\":{\"unicode\":\"🤽\",\"name\":\"person playing water polo\",\"variants\":[{\"unicode\":\"🤽🏻\",\"name\":\"person playing water polo: light skin tone\",\"variants\":[]},{\"unicode\":\"🤽🏼\",\"name\":\"person playing water polo: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤽🏽\",\"name\":\"person playing water polo: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤽🏾\",\"name\":\"person playing water polo: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤽🏿\",\"name\":\"person playing water polo: dark skin tone\",\"variants\":[]}]},\"🤽‍♂️\":{\"unicode\":\"🤽‍♂️\",\"name\":\"man playing water polo\",\"variants\":[]},\"🤽‍♀️\":{\"unicode\":\"🤽‍♀️\",\"name\":\"woman playing water polo\",\"variants\":[]},\"🤾\":{\"unicode\":\"🤾\",\"name\":\"person playing handball\",\"variants\":[{\"unicode\":\"🤾🏻\",\"name\":\"person playing handball: light skin tone\",\"variants\":[]},{\"unicode\":\"🤾🏼\",\"name\":\"person playing handball: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤾🏽\",\"name\":\"person playing handball: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤾🏾\",\"name\":\"person playing handball: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤾🏿\",\"name\":\"person playing handball: dark skin tone\",\"variants\":[]}]},\"🤾‍♂️\":{\"unicode\":\"🤾‍♂️\",\"name\":\"man playing handball\",\"variants\":[]},\"🤾‍♀️\":{\"unicode\":\"🤾‍♀️\",\"name\":\"woman playing handball\",\"variants\":[]},\"🤹\":{\"unicode\":\"🤹\",\"name\":\"person juggling\",\"variants\":[{\"unicode\":\"🤹🏻\",\"name\":\"person juggling: light skin tone\",\"variants\":[]},{\"unicode\":\"🤹🏼\",\"name\":\"person juggling: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🤹🏽\",\"name\":\"person juggling: medium skin tone\",\"variants\":[]},{\"unicode\":\"🤹🏾\",\"name\":\"person juggling: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🤹🏿\",\"name\":\"person juggling: dark skin tone\",\"variants\":[]}]},\"🤹‍♂️\":{\"unicode\":\"🤹‍♂️\",\"name\":\"man juggling\",\"variants\":[]},\"🤹‍♀️\":{\"unicode\":\"🤹‍♀️\",\"name\":\"woman juggling\",\"variants\":[]},\"🧘\":{\"unicode\":\"🧘\",\"name\":\"person in lotus position\",\"variants\":[{\"unicode\":\"🧘🏻\",\"name\":\"person in lotus position: light skin tone\",\"variants\":[]},{\"unicode\":\"🧘🏼\",\"name\":\"person in lotus position: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧘🏽\",\"name\":\"person in lotus position: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧘🏾\",\"name\":\"person in lotus position: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧘🏿\",\"name\":\"person in lotus position: dark skin tone\",\"variants\":[]}]},\"🧘‍♂️\":{\"unicode\":\"🧘‍♂️\",\"name\":\"man in lotus position\",\"variants\":[]},\"🧘‍♀️\":{\"unicode\":\"🧘‍♀️\",\"name\":\"woman in lotus position\",\"variants\":[]},\"🛀\":{\"unicode\":\"🛀\",\"name\":\"person taking bath\",\"variants\":[{\"unicode\":\"🛀🏻\",\"name\":\"person taking bath: light skin tone\",\"variants\":[]},{\"unicode\":\"🛀🏼\",\"name\":\"person taking bath: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🛀🏽\",\"name\":\"person taking bath: medium skin tone\",\"variants\":[]},{\"unicode\":\"🛀🏾\",\"name\":\"person taking bath: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🛀🏿\",\"name\":\"person taking bath: dark skin tone\",\"variants\":[]}]},\"🛌\":{\"unicode\":\"🛌\",\"name\":\"person in bed\",\"variants\":[{\"unicode\":\"🛌🏻\",\"name\":\"person in bed: light skin tone\",\"variants\":[]},{\"unicode\":\"🛌🏼\",\"name\":\"person in bed: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🛌🏽\",\"name\":\"person in bed: medium skin tone\",\"variants\":[]},{\"unicode\":\"🛌🏾\",\"name\":\"person in bed: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🛌🏿\",\"name\":\"person in bed: dark skin tone\",\"variants\":[]}]},\"🧑‍🤝‍🧑\":{\"unicode\":\"🧑‍🤝‍🧑\",\"name\":\"people holding hands\",\"variants\":[{\"unicode\":\"🧑🏻‍🤝‍🧑🏻\",\"name\":\"people holding hands: light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏻‍🤝‍🧑🏼\",\"name\":\"people holding hands: light skin tone, medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏻‍🤝‍🧑🏽\",\"name\":\"people holding hands: light skin tone, medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏻‍🤝‍🧑🏾\",\"name\":\"people holding hands: light skin tone, medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏻‍🤝‍🧑🏿\",\"name\":\"people holding hands: light skin tone, dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🤝‍🧑🏻\",\"name\":\"people holding hands: medium-light skin tone, light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🤝‍🧑🏼\",\"name\":\"people holding hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🤝‍🧑🏽\",\"name\":\"people holding hands: medium-light skin tone, medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🤝‍🧑🏾\",\"name\":\"people holding hands: medium-light skin tone, medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏼‍🤝‍🧑🏿\",\"name\":\"people holding hands: medium-light skin tone, dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🤝‍🧑🏻\",\"name\":\"people holding hands: medium skin tone, light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🤝‍🧑🏼\",\"name\":\"people holding hands: medium skin tone, medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🤝‍🧑🏽\",\"name\":\"people holding hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🤝‍🧑🏾\",\"name\":\"people holding hands: medium skin tone, medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏽‍🤝‍🧑🏿\",\"name\":\"people holding hands: medium skin tone, dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🤝‍🧑🏻\",\"name\":\"people holding hands: medium-dark skin tone, light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🤝‍🧑🏼\",\"name\":\"people holding hands: medium-dark skin tone, medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🤝‍🧑🏽\",\"name\":\"people holding hands: medium-dark skin tone, medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🤝‍🧑🏾\",\"name\":\"people holding hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏾‍🤝‍🧑🏿\",\"name\":\"people holding hands: medium-dark skin tone, dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🤝‍🧑🏻\",\"name\":\"people holding hands: dark skin tone, light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🤝‍🧑🏼\",\"name\":\"people holding hands: dark skin tone, medium-light skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🤝‍🧑🏽\",\"name\":\"people holding hands: dark skin tone, medium skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🤝‍🧑🏾\",\"name\":\"people holding hands: dark skin tone, medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"🧑🏿‍🤝‍🧑🏿\",\"name\":\"people holding hands: dark skin tone\",\"variants\":[]}]},\"👭\":{\"unicode\":\"👭\",\"name\":\"women holding hands\",\"variants\":[{\"unicode\":\"👭🏻\",\"name\":\"women holding hands: light skin tone\",\"variants\":[]},{\"unicode\":\"👭🏼\",\"name\":\"women holding hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👭🏽\",\"name\":\"women holding hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"👭🏾\",\"name\":\"women holding hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👭🏿\",\"name\":\"women holding hands: dark skin tone\",\"variants\":[]}]},\"👫\":{\"unicode\":\"👫\",\"name\":\"woman and man holding hands\",\"variants\":[{\"unicode\":\"👫🏻\",\"name\":\"woman and man holding hands: light skin tone\",\"variants\":[]},{\"unicode\":\"👫🏼\",\"name\":\"woman and man holding hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👫🏽\",\"name\":\"woman and man holding hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"👫🏾\",\"name\":\"woman and man holding hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👫🏿\",\"name\":\"woman and man holding hands: dark skin tone\",\"variants\":[]}]},\"👬\":{\"unicode\":\"👬\",\"name\":\"men holding hands\",\"variants\":[{\"unicode\":\"👬🏻\",\"name\":\"men holding hands: light skin tone\",\"variants\":[]},{\"unicode\":\"👬🏼\",\"name\":\"men holding hands: medium-light skin tone\",\"variants\":[]},{\"unicode\":\"👬🏽\",\"name\":\"men holding hands: medium skin tone\",\"variants\":[]},{\"unicode\":\"👬🏾\",\"name\":\"men holding hands: medium-dark skin tone\",\"variants\":[]},{\"unicode\":\"👬🏿\",\"name\":\"men holding hands: dark skin tone\",\"variants\":[]}]},\"💏\":{\"unicode\":\"💏\",\"name\":\"kiss\",\"variants\":[]},\"👩‍❤️‍💋‍👨\":{\"unicode\":\"👩‍❤️‍💋‍👨\",\"name\":\"kiss: woman, man\",\"variants\":[]},\"👨‍❤️‍💋‍👨\":{\"unicode\":\"👨‍❤️‍💋‍👨\",\"name\":\"kiss: man, man\",\"variants\":[]},\"👩‍❤️‍💋‍👩\":{\"unicode\":\"👩‍❤️‍💋‍👩\",\"name\":\"kiss: woman, woman\",\"variants\":[]},\"💑\":{\"unicode\":\"💑\",\"name\":\"couple with heart\",\"variants\":[]},\"👩‍❤️‍👨\":{\"unicode\":\"👩‍❤️‍👨\",\"name\":\"couple with heart: woman, man\",\"variants\":[]},\"👨‍❤️‍👨\":{\"unicode\":\"👨‍❤️‍👨\",\"name\":\"couple with heart: man, man\",\"variants\":[]},\"👩‍❤️‍👩\":{\"unicode\":\"👩‍❤️‍👩\",\"name\":\"couple with heart: woman, woman\",\"variants\":[]},\"👪\":{\"unicode\":\"👪\",\"name\":\"family\",\"variants\":[]},\"👨‍👩‍👦\":{\"unicode\":\"👨‍👩‍👦\",\"name\":\"family: man, woman, boy\",\"variants\":[]},\"👨‍👩‍👧\":{\"unicode\":\"👨‍👩‍👧\",\"name\":\"family: man, woman, girl\",\"variants\":[]},\"👨‍👩‍👧‍👦\":{\"unicode\":\"👨‍👩‍👧‍👦\",\"name\":\"family: man, woman, girl, boy\",\"variants\":[]},\"👨‍👩‍👦‍👦\":{\"unicode\":\"👨‍👩‍👦‍👦\",\"name\":\"family: man, woman, boy, boy\",\"variants\":[]},\"👨‍👩‍👧‍👧\":{\"unicode\":\"👨‍👩‍👧‍👧\",\"name\":\"family: man, woman, girl, girl\",\"variants\":[]},\"👨‍👨‍👦\":{\"unicode\":\"👨‍👨‍👦\",\"name\":\"family: man, man, boy\",\"variants\":[]},\"👨‍👨‍👧\":{\"unicode\":\"👨‍👨‍👧\",\"name\":\"family: man, man, girl\",\"variants\":[]},\"👨‍👨‍👧‍👦\":{\"unicode\":\"👨‍👨‍👧‍👦\",\"name\":\"family: man, man, girl, boy\",\"variants\":[]},\"👨‍👨‍👦‍👦\":{\"unicode\":\"👨‍👨‍👦‍👦\",\"name\":\"family: man, man, boy, boy\",\"variants\":[]},\"👨‍👨‍👧‍👧\":{\"unicode\":\"👨‍👨‍👧‍👧\",\"name\":\"family: man, man, girl, girl\",\"variants\":[]},\"👩‍👩‍👦\":{\"unicode\":\"👩‍👩‍👦\",\"name\":\"family: woman, woman, boy\",\"variants\":[]},\"👩‍👩‍👧\":{\"unicode\":\"👩‍👩‍👧\",\"name\":\"family: woman, woman, girl\",\"variants\":[]},\"👩‍👩‍👧‍👦\":{\"unicode\":\"👩‍👩‍👧‍👦\",\"name\":\"family: woman, woman, girl, boy\",\"variants\":[]},\"👩‍👩‍👦‍👦\":{\"unicode\":\"👩‍👩‍👦‍👦\",\"name\":\"family: woman, woman, boy, boy\",\"variants\":[]},\"👩‍👩‍👧‍👧\":{\"unicode\":\"👩‍👩‍👧‍👧\",\"name\":\"family: woman, woman, girl, girl\",\"variants\":[]},\"👨‍👦\":{\"unicode\":\"👨‍👦\",\"name\":\"family: man, boy\",\"variants\":[]},\"👨‍👦‍👦\":{\"unicode\":\"👨‍👦‍👦\",\"name\":\"family: man, boy, boy\",\"variants\":[]},\"👨‍👧\":{\"unicode\":\"👨‍👧\",\"name\":\"family: man, girl\",\"variants\":[]},\"👨‍👧‍👦\":{\"unicode\":\"👨‍👧‍👦\",\"name\":\"family: man, girl, boy\",\"variants\":[]},\"👨‍👧‍👧\":{\"unicode\":\"👨‍👧‍👧\",\"name\":\"family: man, girl, girl\",\"variants\":[]},\"👩‍👦\":{\"unicode\":\"👩‍👦\",\"name\":\"family: woman, boy\",\"variants\":[]},\"👩‍👦‍👦\":{\"unicode\":\"👩‍👦‍👦\",\"name\":\"family: woman, boy, boy\",\"variants\":[]},\"👩‍👧\":{\"unicode\":\"👩‍👧\",\"name\":\"family: woman, girl\",\"variants\":[]},\"👩‍👧‍👦\":{\"unicode\":\"👩‍👧‍👦\",\"name\":\"family: woman, girl, boy\",\"variants\":[]},\"👩‍👧‍👧\":{\"unicode\":\"👩‍👧‍👧\",\"name\":\"family: woman, girl, girl\",\"variants\":[]},\"🗣️\":{\"unicode\":\"🗣️\",\"name\":\"speaking head\",\"variants\":[]},\"👤\":{\"unicode\":\"👤\",\"name\":\"bust in silhouette\",\"variants\":[]},\"👥\":{\"unicode\":\"👥\",\"name\":\"busts in silhouette\",\"variants\":[]},\"🫂\":{\"unicode\":\"🫂\",\"name\":\"people hugging\",\"variants\":[]},\"👣\":{\"unicode\":\"👣\",\"name\":\"footprints\",\"variants\":[]}}},\"ANIMALS_AND_NATURE\":{\"type\":\"ANIMALS_AND_NATURE\",\"emojis\":{\"🐵\":{\"unicode\":\"🐵\",\"name\":\"monkey face\",\"variants\":[]},\"🐒\":{\"unicode\":\"🐒\",\"name\":\"monkey\",\"variants\":[]},\"🦍\":{\"unicode\":\"🦍\",\"name\":\"gorilla\",\"variants\":[]},\"🦧\":{\"unicode\":\"🦧\",\"name\":\"orangutan\",\"variants\":[]},\"🐶\":{\"unicode\":\"🐶\",\"name\":\"dog face\",\"variants\":[]},\"🐕\":{\"unicode\":\"🐕\",\"name\":\"dog\",\"variants\":[]},\"🦮\":{\"unicode\":\"🦮\",\"name\":\"guide dog\",\"variants\":[]},\"🐕‍🦺\":{\"unicode\":\"🐕‍🦺\",\"name\":\"service dog\",\"variants\":[]},\"🐩\":{\"unicode\":\"🐩\",\"name\":\"poodle\",\"variants\":[]},\"🐺\":{\"unicode\":\"🐺\",\"name\":\"wolf\",\"variants\":[]},\"🦊\":{\"unicode\":\"🦊\",\"name\":\"fox\",\"variants\":[]},\"🦝\":{\"unicode\":\"🦝\",\"name\":\"raccoon\",\"variants\":[]},\"🐱\":{\"unicode\":\"🐱\",\"name\":\"cat face\",\"variants\":[]},\"🐈\":{\"unicode\":\"🐈\",\"name\":\"cat\",\"variants\":[]},\"🐈‍⬛\":{\"unicode\":\"🐈‍⬛\",\"name\":\"black cat\",\"variants\":[]},\"🦁\":{\"unicode\":\"🦁\",\"name\":\"lion\",\"variants\":[]},\"🐯\":{\"unicode\":\"🐯\",\"name\":\"tiger face\",\"variants\":[]},\"🐅\":{\"unicode\":\"🐅\",\"name\":\"tiger\",\"variants\":[]},\"🐆\":{\"unicode\":\"🐆\",\"name\":\"leopard\",\"variants\":[]},\"🐴\":{\"unicode\":\"🐴\",\"name\":\"horse face\",\"variants\":[]},\"🐎\":{\"unicode\":\"🐎\",\"name\":\"horse\",\"variants\":[]},\"🦄\":{\"unicode\":\"🦄\",\"name\":\"unicorn\",\"variants\":[]},\"🦓\":{\"unicode\":\"🦓\",\"name\":\"zebra\",\"variants\":[]},\"🦌\":{\"unicode\":\"🦌\",\"name\":\"deer\",\"variants\":[]},\"🦬\":{\"unicode\":\"🦬\",\"name\":\"bison\",\"variants\":[]},\"🐮\":{\"unicode\":\"🐮\",\"name\":\"cow face\",\"variants\":[]},\"🐂\":{\"unicode\":\"🐂\",\"name\":\"ox\",\"variants\":[]},\"🐃\":{\"unicode\":\"🐃\",\"name\":\"water buffalo\",\"variants\":[]},\"🐄\":{\"unicode\":\"🐄\",\"name\":\"cow\",\"variants\":[]},\"🐷\":{\"unicode\":\"🐷\",\"name\":\"pig face\",\"variants\":[]},\"🐖\":{\"unicode\":\"🐖\",\"name\":\"pig\",\"variants\":[]},\"🐗\":{\"unicode\":\"🐗\",\"name\":\"boar\",\"variants\":[]},\"🐽\":{\"unicode\":\"🐽\",\"name\":\"pig nose\",\"variants\":[]},\"🐏\":{\"unicode\":\"🐏\",\"name\":\"ram\",\"variants\":[]},\"🐑\":{\"unicode\":\"🐑\",\"name\":\"ewe\",\"variants\":[]},\"🐐\":{\"unicode\":\"🐐\",\"name\":\"goat\",\"variants\":[]},\"🐪\":{\"unicode\":\"🐪\",\"name\":\"camel\",\"variants\":[]},\"🐫\":{\"unicode\":\"🐫\",\"name\":\"two-hump camel\",\"variants\":[]},\"🦙\":{\"unicode\":\"🦙\",\"name\":\"llama\",\"variants\":[]},\"🦒\":{\"unicode\":\"🦒\",\"name\":\"giraffe\",\"variants\":[]},\"🐘\":{\"unicode\":\"🐘\",\"name\":\"elephant\",\"variants\":[]},\"🦣\":{\"unicode\":\"🦣\",\"name\":\"mammoth\",\"variants\":[]},\"🦏\":{\"unicode\":\"🦏\",\"name\":\"rhinoceros\",\"variants\":[]},\"🦛\":{\"unicode\":\"🦛\",\"name\":\"hippopotamus\",\"variants\":[]},\"🐭\":{\"unicode\":\"🐭\",\"name\":\"mouse face\",\"variants\":[]},\"🐁\":{\"unicode\":\"🐁\",\"name\":\"mouse\",\"variants\":[]},\"🐀\":{\"unicode\":\"🐀\",\"name\":\"rat\",\"variants\":[]},\"🐹\":{\"unicode\":\"🐹\",\"name\":\"hamster\",\"variants\":[]},\"🐰\":{\"unicode\":\"🐰\",\"name\":\"rabbit face\",\"variants\":[]},\"🐇\":{\"unicode\":\"🐇\",\"name\":\"rabbit\",\"variants\":[]},\"🐿️\":{\"unicode\":\"🐿️\",\"name\":\"chipmunk\",\"variants\":[]},\"🦫\":{\"unicode\":\"🦫\",\"name\":\"beaver\",\"variants\":[]},\"🦔\":{\"unicode\":\"🦔\",\"name\":\"hedgehog\",\"variants\":[]},\"🦇\":{\"unicode\":\"🦇\",\"name\":\"bat\",\"variants\":[]},\"🐻\":{\"unicode\":\"🐻\",\"name\":\"bear\",\"variants\":[]},\"🐻‍❄️\":{\"unicode\":\"🐻‍❄️\",\"name\":\"polar bear\",\"variants\":[]},\"🐨\":{\"unicode\":\"🐨\",\"name\":\"koala\",\"variants\":[]},\"🐼\":{\"unicode\":\"🐼\",\"name\":\"panda\",\"variants\":[]},\"🦥\":{\"unicode\":\"🦥\",\"name\":\"sloth\",\"variants\":[]},\"🦦\":{\"unicode\":\"🦦\",\"name\":\"otter\",\"variants\":[]},\"🦨\":{\"unicode\":\"🦨\",\"name\":\"skunk\",\"variants\":[]},\"🦘\":{\"unicode\":\"🦘\",\"name\":\"kangaroo\",\"variants\":[]},\"🦡\":{\"unicode\":\"🦡\",\"name\":\"badger\",\"variants\":[]},\"🐾\":{\"unicode\":\"🐾\",\"name\":\"paw prints\",\"variants\":[]},\"🦃\":{\"unicode\":\"🦃\",\"name\":\"turkey\",\"variants\":[]},\"🐔\":{\"unicode\":\"🐔\",\"name\":\"chicken\",\"variants\":[]},\"🐓\":{\"unicode\":\"🐓\",\"name\":\"rooster\",\"variants\":[]},\"🐣\":{\"unicode\":\"🐣\",\"name\":\"hatching chick\",\"variants\":[]},\"🐤\":{\"unicode\":\"🐤\",\"name\":\"baby chick\",\"variants\":[]},\"🐥\":{\"unicode\":\"🐥\",\"name\":\"front-facing baby chick\",\"variants\":[]},\"🐦\":{\"unicode\":\"🐦\",\"name\":\"bird\",\"variants\":[]},\"🐧\":{\"unicode\":\"🐧\",\"name\":\"penguin\",\"variants\":[]},\"🕊️\":{\"unicode\":\"🕊️\",\"name\":\"dove\",\"variants\":[]},\"🦅\":{\"unicode\":\"🦅\",\"name\":\"eagle\",\"variants\":[]},\"🦆\":{\"unicode\":\"🦆\",\"name\":\"duck\",\"variants\":[]},\"🦢\":{\"unicode\":\"🦢\",\"name\":\"swan\",\"variants\":[]},\"🦉\":{\"unicode\":\"🦉\",\"name\":\"owl\",\"variants\":[]},\"🦤\":{\"unicode\":\"🦤\",\"name\":\"dodo\",\"variants\":[]},\"🪶\":{\"unicode\":\"🪶\",\"name\":\"feather\",\"variants\":[]},\"🦩\":{\"unicode\":\"🦩\",\"name\":\"flamingo\",\"variants\":[]},\"🦚\":{\"unicode\":\"🦚\",\"name\":\"peacock\",\"variants\":[]},\"🦜\":{\"unicode\":\"🦜\",\"name\":\"parrot\",\"variants\":[]},\"🐸\":{\"unicode\":\"🐸\",\"name\":\"frog\",\"variants\":[]},\"🐊\":{\"unicode\":\"🐊\",\"name\":\"crocodile\",\"variants\":[]},\"🐢\":{\"unicode\":\"🐢\",\"name\":\"turtle\",\"variants\":[]},\"🦎\":{\"unicode\":\"🦎\",\"name\":\"lizard\",\"variants\":[]},\"🐍\":{\"unicode\":\"🐍\",\"name\":\"snake\",\"variants\":[]},\"🐲\":{\"unicode\":\"🐲\",\"name\":\"dragon face\",\"variants\":[]},\"🐉\":{\"unicode\":\"🐉\",\"name\":\"dragon\",\"variants\":[]},\"🦕\":{\"unicode\":\"🦕\",\"name\":\"sauropod\",\"variants\":[]},\"🦖\":{\"unicode\":\"🦖\",\"name\":\"T-Rex\",\"variants\":[]},\"🐳\":{\"unicode\":\"🐳\",\"name\":\"spouting whale\",\"variants\":[]},\"🐋\":{\"unicode\":\"🐋\",\"name\":\"whale\",\"variants\":[]},\"🐬\":{\"unicode\":\"🐬\",\"name\":\"dolphin\",\"variants\":[]},\"🦭\":{\"unicode\":\"🦭\",\"name\":\"seal\",\"variants\":[]},\"🐟\":{\"unicode\":\"🐟\",\"name\":\"fish\",\"variants\":[]},\"🐠\":{\"unicode\":\"🐠\",\"name\":\"tropical fish\",\"variants\":[]},\"🐡\":{\"unicode\":\"🐡\",\"name\":\"blowfish\",\"variants\":[]},\"🦈\":{\"unicode\":\"🦈\",\"name\":\"shark\",\"variants\":[]},\"🐙\":{\"unicode\":\"🐙\",\"name\":\"octopus\",\"variants\":[]},\"🐚\":{\"unicode\":\"🐚\",\"name\":\"spiral shell\",\"variants\":[]},\"🐌\":{\"unicode\":\"🐌\",\"name\":\"snail\",\"variants\":[]},\"🦋\":{\"unicode\":\"🦋\",\"name\":\"butterfly\",\"variants\":[]},\"🐛\":{\"unicode\":\"🐛\",\"name\":\"bug\",\"variants\":[]},\"🐜\":{\"unicode\":\"🐜\",\"name\":\"ant\",\"variants\":[]},\"🐝\":{\"unicode\":\"🐝\",\"name\":\"honeybee\",\"variants\":[]},\"🪲\":{\"unicode\":\"🪲\",\"name\":\"beetle\",\"variants\":[]},\"🐞\":{\"unicode\":\"🐞\",\"name\":\"lady beetle\",\"variants\":[]},\"🦗\":{\"unicode\":\"🦗\",\"name\":\"cricket\",\"variants\":[]},\"🪳\":{\"unicode\":\"🪳\",\"name\":\"cockroach\",\"variants\":[]},\"🕷️\":{\"unicode\":\"🕷️\",\"name\":\"spider\",\"variants\":[]},\"🕸️\":{\"unicode\":\"🕸️\",\"name\":\"spider web\",\"variants\":[]},\"🦂\":{\"unicode\":\"🦂\",\"name\":\"scorpion\",\"variants\":[]},\"🦟\":{\"unicode\":\"🦟\",\"name\":\"mosquito\",\"variants\":[]},\"🪰\":{\"unicode\":\"🪰\",\"name\":\"fly\",\"variants\":[]},\"🪱\":{\"unicode\":\"🪱\",\"name\":\"worm\",\"variants\":[]},\"🦠\":{\"unicode\":\"🦠\",\"name\":\"microbe\",\"variants\":[]},\"💐\":{\"unicode\":\"💐\",\"name\":\"bouquet\",\"variants\":[]},\"🌸\":{\"unicode\":\"🌸\",\"name\":\"cherry blossom\",\"variants\":[]},\"💮\":{\"unicode\":\"💮\",\"name\":\"white flower\",\"variants\":[]},\"🏵️\":{\"unicode\":\"🏵️\",\"name\":\"rosette\",\"variants\":[]},\"🌹\":{\"unicode\":\"🌹\",\"name\":\"rose\",\"variants\":[]},\"🥀\":{\"unicode\":\"🥀\",\"name\":\"wilted flower\",\"variants\":[]},\"🌺\":{\"unicode\":\"🌺\",\"name\":\"hibiscus\",\"variants\":[]},\"🌻\":{\"unicode\":\"🌻\",\"name\":\"sunflower\",\"variants\":[]},\"🌼\":{\"unicode\":\"🌼\",\"name\":\"blossom\",\"variants\":[]},\"🌷\":{\"unicode\":\"🌷\",\"name\":\"tulip\",\"variants\":[]},\"🌱\":{\"unicode\":\"🌱\",\"name\":\"seedling\",\"variants\":[]},\"🪴\":{\"unicode\":\"🪴\",\"name\":\"potted plant\",\"variants\":[]},\"🌲\":{\"unicode\":\"🌲\",\"name\":\"evergreen tree\",\"variants\":[]},\"🌳\":{\"unicode\":\"🌳\",\"name\":\"deciduous tree\",\"variants\":[]},\"🌴\":{\"unicode\":\"🌴\",\"name\":\"palm tree\",\"variants\":[]},\"🌵\":{\"unicode\":\"🌵\",\"name\":\"cactus\",\"variants\":[]},\"🌾\":{\"unicode\":\"🌾\",\"name\":\"sheaf of rice\",\"variants\":[]},\"🌿\":{\"unicode\":\"🌿\",\"name\":\"herb\",\"variants\":[]},\"☘️\":{\"unicode\":\"☘️\",\"name\":\"shamrock\",\"variants\":[]},\"🍀\":{\"unicode\":\"🍀\",\"name\":\"four leaf clover\",\"variants\":[]},\"🍁\":{\"unicode\":\"🍁\",\"name\":\"maple leaf\",\"variants\":[]},\"🍂\":{\"unicode\":\"🍂\",\"name\":\"fallen leaf\",\"variants\":[]},\"🍃\":{\"unicode\":\"🍃\",\"name\":\"leaf fluttering in wind\",\"variants\":[]}}},\"FOOD_AND_DRINK\":{\"type\":\"FOOD_AND_DRINK\",\"emojis\":{\"🍇\":{\"unicode\":\"🍇\",\"name\":\"grapes\",\"variants\":[]},\"🍈\":{\"unicode\":\"🍈\",\"name\":\"melon\",\"variants\":[]},\"🍉\":{\"unicode\":\"🍉\",\"name\":\"watermelon\",\"variants\":[]},\"🍊\":{\"unicode\":\"🍊\",\"name\":\"tangerine\",\"variants\":[]},\"🍋\":{\"unicode\":\"🍋\",\"name\":\"lemon\",\"variants\":[]},\"🍌\":{\"unicode\":\"🍌\",\"name\":\"banana\",\"variants\":[]},\"🍍\":{\"unicode\":\"🍍\",\"name\":\"pineapple\",\"variants\":[]},\"🥭\":{\"unicode\":\"🥭\",\"name\":\"mango\",\"variants\":[]},\"🍎\":{\"unicode\":\"🍎\",\"name\":\"red apple\",\"variants\":[]},\"🍏\":{\"unicode\":\"🍏\",\"name\":\"green apple\",\"variants\":[]},\"🍐\":{\"unicode\":\"🍐\",\"name\":\"pear\",\"variants\":[]},\"🍑\":{\"unicode\":\"🍑\",\"name\":\"peach\",\"variants\":[]},\"🍒\":{\"unicode\":\"🍒\",\"name\":\"cherries\",\"variants\":[]},\"🍓\":{\"unicode\":\"🍓\",\"name\":\"strawberry\",\"variants\":[]},\"🫐\":{\"unicode\":\"🫐\",\"name\":\"blueberries\",\"variants\":[]},\"🥝\":{\"unicode\":\"🥝\",\"name\":\"kiwi fruit\",\"variants\":[]},\"🍅\":{\"unicode\":\"🍅\",\"name\":\"tomato\",\"variants\":[]},\"🫒\":{\"unicode\":\"🫒\",\"name\":\"olive\",\"variants\":[]},\"🥥\":{\"unicode\":\"🥥\",\"name\":\"coconut\",\"variants\":[]},\"🥑\":{\"unicode\":\"🥑\",\"name\":\"avocado\",\"variants\":[]},\"🍆\":{\"unicode\":\"🍆\",\"name\":\"eggplant\",\"variants\":[]},\"🥔\":{\"unicode\":\"🥔\",\"name\":\"potato\",\"variants\":[]},\"🥕\":{\"unicode\":\"🥕\",\"name\":\"carrot\",\"variants\":[]},\"🌽\":{\"unicode\":\"🌽\",\"name\":\"ear of corn\",\"variants\":[]},\"🌶️\":{\"unicode\":\"🌶️\",\"name\":\"hot pepper\",\"variants\":[]},\"🫑\":{\"unicode\":\"🫑\",\"name\":\"bell pepper\",\"variants\":[]},\"🥒\":{\"unicode\":\"🥒\",\"name\":\"cucumber\",\"variants\":[]},\"🥬\":{\"unicode\":\"🥬\",\"name\":\"leafy green\",\"variants\":[]},\"🥦\":{\"unicode\":\"🥦\",\"name\":\"broccoli\",\"variants\":[]},\"🧄\":{\"unicode\":\"🧄\",\"name\":\"garlic\",\"variants\":[]},\"🧅\":{\"unicode\":\"🧅\",\"name\":\"onion\",\"variants\":[]},\"🍄\":{\"unicode\":\"🍄\",\"name\":\"mushroom\",\"variants\":[]},\"🥜\":{\"unicode\":\"🥜\",\"name\":\"peanuts\",\"variants\":[]},\"🌰\":{\"unicode\":\"🌰\",\"name\":\"chestnut\",\"variants\":[]},\"🍞\":{\"unicode\":\"🍞\",\"name\":\"bread\",\"variants\":[]},\"🥐\":{\"unicode\":\"🥐\",\"name\":\"croissant\",\"variants\":[]},\"🥖\":{\"unicode\":\"🥖\",\"name\":\"baguette bread\",\"variants\":[]},\"🫓\":{\"unicode\":\"🫓\",\"name\":\"flatbread\",\"variants\":[]},\"🥨\":{\"unicode\":\"🥨\",\"name\":\"pretzel\",\"variants\":[]},\"🥯\":{\"unicode\":\"🥯\",\"name\":\"bagel\",\"variants\":[]},\"🥞\":{\"unicode\":\"🥞\",\"name\":\"pancakes\",\"variants\":[]},\"🧇\":{\"unicode\":\"🧇\",\"name\":\"waffle\",\"variants\":[]},\"🧀\":{\"unicode\":\"🧀\",\"name\":\"cheese wedge\",\"variants\":[]},\"🍖\":{\"unicode\":\"🍖\",\"name\":\"meat on bone\",\"variants\":[]},\"🍗\":{\"unicode\":\"🍗\",\"name\":\"poultry leg\",\"variants\":[]},\"🥩\":{\"unicode\":\"🥩\",\"name\":\"cut of meat\",\"variants\":[]},\"🥓\":{\"unicode\":\"🥓\",\"name\":\"bacon\",\"variants\":[]},\"🍔\":{\"unicode\":\"🍔\",\"name\":\"hamburger\",\"variants\":[]},\"🍟\":{\"unicode\":\"🍟\",\"name\":\"french fries\",\"variants\":[]},\"🍕\":{\"unicode\":\"🍕\",\"name\":\"pizza\",\"variants\":[]},\"🌭\":{\"unicode\":\"🌭\",\"name\":\"hot dog\",\"variants\":[]},\"🥪\":{\"unicode\":\"🥪\",\"name\":\"sandwich\",\"variants\":[]},\"🌮\":{\"unicode\":\"🌮\",\"name\":\"taco\",\"variants\":[]},\"🌯\":{\"unicode\":\"🌯\",\"name\":\"burrito\",\"variants\":[]},\"🫔\":{\"unicode\":\"🫔\",\"name\":\"tamale\",\"variants\":[]},\"🥙\":{\"unicode\":\"🥙\",\"name\":\"stuffed flatbread\",\"variants\":[]},\"🧆\":{\"unicode\":\"🧆\",\"name\":\"falafel\",\"variants\":[]},\"🥚\":{\"unicode\":\"🥚\",\"name\":\"egg\",\"variants\":[]},\"🍳\":{\"unicode\":\"🍳\",\"name\":\"cooking\",\"variants\":[]},\"🥘\":{\"unicode\":\"🥘\",\"name\":\"shallow pan of food\",\"variants\":[]},\"🍲\":{\"unicode\":\"🍲\",\"name\":\"pot of food\",\"variants\":[]},\"🫕\":{\"unicode\":\"🫕\",\"name\":\"fondue\",\"variants\":[]},\"🥣\":{\"unicode\":\"🥣\",\"name\":\"bowl with spoon\",\"variants\":[]},\"🥗\":{\"unicode\":\"🥗\",\"name\":\"green salad\",\"variants\":[]},\"🍿\":{\"unicode\":\"🍿\",\"name\":\"popcorn\",\"variants\":[]},\"🧈\":{\"unicode\":\"🧈\",\"name\":\"butter\",\"variants\":[]},\"🧂\":{\"unicode\":\"🧂\",\"name\":\"salt\",\"variants\":[]},\"🥫\":{\"unicode\":\"🥫\",\"name\":\"canned food\",\"variants\":[]},\"🍱\":{\"unicode\":\"🍱\",\"name\":\"bento box\",\"variants\":[]},\"🍘\":{\"unicode\":\"🍘\",\"name\":\"rice cracker\",\"variants\":[]},\"🍙\":{\"unicode\":\"🍙\",\"name\":\"rice ball\",\"variants\":[]},\"🍚\":{\"unicode\":\"🍚\",\"name\":\"cooked rice\",\"variants\":[]},\"🍛\":{\"unicode\":\"🍛\",\"name\":\"curry rice\",\"variants\":[]},\"🍜\":{\"unicode\":\"🍜\",\"name\":\"steaming bowl\",\"variants\":[]},\"🍝\":{\"unicode\":\"🍝\",\"name\":\"spaghetti\",\"variants\":[]},\"🍠\":{\"unicode\":\"🍠\",\"name\":\"roasted sweet potato\",\"variants\":[]},\"🍢\":{\"unicode\":\"🍢\",\"name\":\"oden\",\"variants\":[]},\"🍣\":{\"unicode\":\"🍣\",\"name\":\"sushi\",\"variants\":[]},\"🍤\":{\"unicode\":\"🍤\",\"name\":\"fried shrimp\",\"variants\":[]},\"🍥\":{\"unicode\":\"🍥\",\"name\":\"fish cake with swirl\",\"variants\":[]},\"🥮\":{\"unicode\":\"🥮\",\"name\":\"moon cake\",\"variants\":[]},\"🍡\":{\"unicode\":\"🍡\",\"name\":\"dango\",\"variants\":[]},\"🥟\":{\"unicode\":\"🥟\",\"name\":\"dumpling\",\"variants\":[]},\"🥠\":{\"unicode\":\"🥠\",\"name\":\"fortune cookie\",\"variants\":[]},\"🥡\":{\"unicode\":\"🥡\",\"name\":\"takeout box\",\"variants\":[]},\"🦀\":{\"unicode\":\"🦀\",\"name\":\"crab\",\"variants\":[]},\"🦞\":{\"unicode\":\"🦞\",\"name\":\"lobster\",\"variants\":[]},\"🦐\":{\"unicode\":\"🦐\",\"name\":\"shrimp\",\"variants\":[]},\"🦑\":{\"unicode\":\"🦑\",\"name\":\"squid\",\"variants\":[]},\"🦪\":{\"unicode\":\"🦪\",\"name\":\"oyster\",\"variants\":[]},\"🍦\":{\"unicode\":\"🍦\",\"name\":\"soft ice cream\",\"variants\":[]},\"🍧\":{\"unicode\":\"🍧\",\"name\":\"shaved ice\",\"variants\":[]},\"🍨\":{\"unicode\":\"🍨\",\"name\":\"ice cream\",\"variants\":[]},\"🍩\":{\"unicode\":\"🍩\",\"name\":\"doughnut\",\"variants\":[]},\"🍪\":{\"unicode\":\"🍪\",\"name\":\"cookie\",\"variants\":[]},\"🎂\":{\"unicode\":\"🎂\",\"name\":\"birthday cake\",\"variants\":[]},\"🍰\":{\"unicode\":\"🍰\",\"name\":\"shortcake\",\"variants\":[]},\"🧁\":{\"unicode\":\"🧁\",\"name\":\"cupcake\",\"variants\":[]},\"🥧\":{\"unicode\":\"🥧\",\"name\":\"pie\",\"variants\":[]},\"🍫\":{\"unicode\":\"🍫\",\"name\":\"chocolate bar\",\"variants\":[]},\"🍬\":{\"unicode\":\"🍬\",\"name\":\"candy\",\"variants\":[]},\"🍭\":{\"unicode\":\"🍭\",\"name\":\"lollipop\",\"variants\":[]},\"🍮\":{\"unicode\":\"🍮\",\"name\":\"custard\",\"variants\":[]},\"🍯\":{\"unicode\":\"🍯\",\"name\":\"honey pot\",\"variants\":[]},\"🍼\":{\"unicode\":\"🍼\",\"name\":\"baby bottle\",\"variants\":[]},\"🥛\":{\"unicode\":\"🥛\",\"name\":\"glass of milk\",\"variants\":[]},\"☕\":{\"unicode\":\"☕\",\"name\":\"hot beverage\",\"variants\":[]},\"🫖\":{\"unicode\":\"🫖\",\"name\":\"teapot\",\"variants\":[]},\"🍵\":{\"unicode\":\"🍵\",\"name\":\"teacup without handle\",\"variants\":[]},\"🍶\":{\"unicode\":\"🍶\",\"name\":\"sake\",\"variants\":[]},\"🍾\":{\"unicode\":\"🍾\",\"name\":\"bottle with popping cork\",\"variants\":[]},\"🍷\":{\"unicode\":\"🍷\",\"name\":\"wine glass\",\"variants\":[]},\"🍸\":{\"unicode\":\"🍸\",\"name\":\"cocktail glass\",\"variants\":[]},\"🍹\":{\"unicode\":\"🍹\",\"name\":\"tropical drink\",\"variants\":[]},\"🍺\":{\"unicode\":\"🍺\",\"name\":\"beer mug\",\"variants\":[]},\"🍻\":{\"unicode\":\"🍻\",\"name\":\"clinking beer mugs\",\"variants\":[]},\"🥂\":{\"unicode\":\"🥂\",\"name\":\"clinking glasses\",\"variants\":[]},\"🥃\":{\"unicode\":\"🥃\",\"name\":\"tumbler glass\",\"variants\":[]},\"🥤\":{\"unicode\":\"🥤\",\"name\":\"cup with straw\",\"variants\":[]},\"🧋\":{\"unicode\":\"🧋\",\"name\":\"bubble tea\",\"variants\":[]},\"🧃\":{\"unicode\":\"🧃\",\"name\":\"beverage box\",\"variants\":[]},\"🧉\":{\"unicode\":\"🧉\",\"name\":\"mate\",\"variants\":[]},\"🧊\":{\"unicode\":\"🧊\",\"name\":\"ice\",\"variants\":[]},\"🥢\":{\"unicode\":\"🥢\",\"name\":\"chopsticks\",\"variants\":[]},\"🍽️\":{\"unicode\":\"🍽️\",\"name\":\"fork and knife with plate\",\"variants\":[]},\"🍴\":{\"unicode\":\"🍴\",\"name\":\"fork and knife\",\"variants\":[]},\"🥄\":{\"unicode\":\"🥄\",\"name\":\"spoon\",\"variants\":[]},\"🔪\":{\"unicode\":\"🔪\",\"name\":\"kitchen knife\",\"variants\":[]},\"🏺\":{\"unicode\":\"🏺\",\"name\":\"amphora\",\"variants\":[]}}},\"TRAVEL_AND_PLACES\":{\"type\":\"TRAVEL_AND_PLACES\",\"emojis\":{\"🌍\":{\"unicode\":\"🌍\",\"name\":\"globe showing Europe-Africa\",\"variants\":[]},\"🌎\":{\"unicode\":\"🌎\",\"name\":\"globe showing Americas\",\"variants\":[]},\"🌏\":{\"unicode\":\"🌏\",\"name\":\"globe showing Asia-Australia\",\"variants\":[]},\"🌐\":{\"unicode\":\"🌐\",\"name\":\"globe with meridians\",\"variants\":[]},\"🗺️\":{\"unicode\":\"🗺️\",\"name\":\"world map\",\"variants\":[]},\"🗾\":{\"unicode\":\"🗾\",\"name\":\"map of Japan\",\"variants\":[]},\"🧭\":{\"unicode\":\"🧭\",\"name\":\"compass\",\"variants\":[]},\"🏔️\":{\"unicode\":\"🏔️\",\"name\":\"snow-capped mountain\",\"variants\":[]},\"⛰️\":{\"unicode\":\"⛰️\",\"name\":\"mountain\",\"variants\":[]},\"🌋\":{\"unicode\":\"🌋\",\"name\":\"volcano\",\"variants\":[]},\"🗻\":{\"unicode\":\"🗻\",\"name\":\"mount fuji\",\"variants\":[]},\"🏕️\":{\"unicode\":\"🏕️\",\"name\":\"camping\",\"variants\":[]},\"🏖️\":{\"unicode\":\"🏖️\",\"name\":\"beach with umbrella\",\"variants\":[]},\"🏜️\":{\"unicode\":\"🏜️\",\"name\":\"desert\",\"variants\":[]},\"🏝️\":{\"unicode\":\"🏝️\",\"name\":\"desert island\",\"variants\":[]},\"🏞️\":{\"unicode\":\"🏞️\",\"name\":\"national park\",\"variants\":[]},\"🏟️\":{\"unicode\":\"🏟️\",\"name\":\"stadium\",\"variants\":[]},\"🏛️\":{\"unicode\":\"🏛️\",\"name\":\"classical building\",\"variants\":[]},\"🏗️\":{\"unicode\":\"🏗️\",\"name\":\"building construction\",\"variants\":[]},\"🧱\":{\"unicode\":\"🧱\",\"name\":\"brick\",\"variants\":[]},\"🪨\":{\"unicode\":\"🪨\",\"name\":\"rock\",\"variants\":[]},\"🪵\":{\"unicode\":\"🪵\",\"name\":\"wood\",\"variants\":[]},\"🛖\":{\"unicode\":\"🛖\",\"name\":\"hut\",\"variants\":[]},\"🏘️\":{\"unicode\":\"🏘️\",\"name\":\"houses\",\"variants\":[]},\"🏚️\":{\"unicode\":\"🏚️\",\"name\":\"derelict house\",\"variants\":[]},\"🏠\":{\"unicode\":\"🏠\",\"name\":\"house\",\"variants\":[]},\"🏡\":{\"unicode\":\"🏡\",\"name\":\"house with garden\",\"variants\":[]},\"🏢\":{\"unicode\":\"🏢\",\"name\":\"office building\",\"variants\":[]},\"🏣\":{\"unicode\":\"🏣\",\"name\":\"Japanese post office\",\"variants\":[]},\"🏤\":{\"unicode\":\"🏤\",\"name\":\"post office\",\"variants\":[]},\"🏥\":{\"unicode\":\"🏥\",\"name\":\"hospital\",\"variants\":[]},\"🏦\":{\"unicode\":\"🏦\",\"name\":\"bank\",\"variants\":[]},\"🏨\":{\"unicode\":\"🏨\",\"name\":\"hotel\",\"variants\":[]},\"🏩\":{\"unicode\":\"🏩\",\"name\":\"love hotel\",\"variants\":[]},\"🏪\":{\"unicode\":\"🏪\",\"name\":\"convenience store\",\"variants\":[]},\"🏫\":{\"unicode\":\"🏫\",\"name\":\"school\",\"variants\":[]},\"🏬\":{\"unicode\":\"🏬\",\"name\":\"department store\",\"variants\":[]},\"🏭\":{\"unicode\":\"🏭\",\"name\":\"factory\",\"variants\":[]},\"🏯\":{\"unicode\":\"🏯\",\"name\":\"Japanese castle\",\"variants\":[]},\"🏰\":{\"unicode\":\"🏰\",\"name\":\"castle\",\"variants\":[]},\"💒\":{\"unicode\":\"💒\",\"name\":\"wedding\",\"variants\":[]},\"🗼\":{\"unicode\":\"🗼\",\"name\":\"Tokyo tower\",\"variants\":[]},\"🗽\":{\"unicode\":\"🗽\",\"name\":\"Statue of Liberty\",\"variants\":[]},\"⛪\":{\"unicode\":\"⛪\",\"name\":\"church\",\"variants\":[]},\"🕌\":{\"unicode\":\"🕌\",\"name\":\"mosque\",\"variants\":[]},\"🛕\":{\"unicode\":\"🛕\",\"name\":\"hindu temple\",\"variants\":[]},\"🕍\":{\"unicode\":\"🕍\",\"name\":\"synagogue\",\"variants\":[]},\"⛩️\":{\"unicode\":\"⛩️\",\"name\":\"shinto shrine\",\"variants\":[]},\"🕋\":{\"unicode\":\"🕋\",\"name\":\"kaaba\",\"variants\":[]},\"⛲\":{\"unicode\":\"⛲\",\"name\":\"fountain\",\"variants\":[]},\"⛺\":{\"unicode\":\"⛺\",\"name\":\"tent\",\"variants\":[]},\"🌁\":{\"unicode\":\"🌁\",\"name\":\"foggy\",\"variants\":[]},\"🌃\":{\"unicode\":\"🌃\",\"name\":\"night with stars\",\"variants\":[]},\"🏙️\":{\"unicode\":\"🏙️\",\"name\":\"cityscape\",\"variants\":[]},\"🌄\":{\"unicode\":\"🌄\",\"name\":\"sunrise over mountains\",\"variants\":[]},\"🌅\":{\"unicode\":\"🌅\",\"name\":\"sunrise\",\"variants\":[]},\"🌆\":{\"unicode\":\"🌆\",\"name\":\"cityscape at dusk\",\"variants\":[]},\"🌇\":{\"unicode\":\"🌇\",\"name\":\"sunset\",\"variants\":[]},\"🌉\":{\"unicode\":\"🌉\",\"name\":\"bridge at night\",\"variants\":[]},\"♨️\":{\"unicode\":\"♨️\",\"name\":\"hot springs\",\"variants\":[]},\"🎠\":{\"unicode\":\"🎠\",\"name\":\"carousel horse\",\"variants\":[]},\"🎡\":{\"unicode\":\"🎡\",\"name\":\"ferris wheel\",\"variants\":[]},\"🎢\":{\"unicode\":\"🎢\",\"name\":\"roller coaster\",\"variants\":[]},\"💈\":{\"unicode\":\"💈\",\"name\":\"barber pole\",\"variants\":[]},\"🎪\":{\"unicode\":\"🎪\",\"name\":\"circus tent\",\"variants\":[]},\"🚂\":{\"unicode\":\"🚂\",\"name\":\"locomotive\",\"variants\":[]},\"🚃\":{\"unicode\":\"🚃\",\"name\":\"railway car\",\"variants\":[]},\"🚄\":{\"unicode\":\"🚄\",\"name\":\"high-speed train\",\"variants\":[]},\"🚅\":{\"unicode\":\"🚅\",\"name\":\"bullet train\",\"variants\":[]},\"🚆\":{\"unicode\":\"🚆\",\"name\":\"train\",\"variants\":[]},\"🚇\":{\"unicode\":\"🚇\",\"name\":\"metro\",\"variants\":[]},\"🚈\":{\"unicode\":\"🚈\",\"name\":\"light rail\",\"variants\":[]},\"🚉\":{\"unicode\":\"🚉\",\"name\":\"station\",\"variants\":[]},\"🚊\":{\"unicode\":\"🚊\",\"name\":\"tram\",\"variants\":[]},\"🚝\":{\"unicode\":\"🚝\",\"name\":\"monorail\",\"variants\":[]},\"🚞\":{\"unicode\":\"🚞\",\"name\":\"mountain railway\",\"variants\":[]},\"🚋\":{\"unicode\":\"🚋\",\"name\":\"tram car\",\"variants\":[]},\"🚌\":{\"unicode\":\"🚌\",\"name\":\"bus\",\"variants\":[]},\"🚍\":{\"unicode\":\"🚍\",\"name\":\"oncoming bus\",\"variants\":[]},\"🚎\":{\"unicode\":\"🚎\",\"name\":\"trolleybus\",\"variants\":[]},\"🚐\":{\"unicode\":\"🚐\",\"name\":\"minibus\",\"variants\":[]},\"🚑\":{\"unicode\":\"🚑\",\"name\":\"ambulance\",\"variants\":[]},\"🚒\":{\"unicode\":\"🚒\",\"name\":\"fire engine\",\"variants\":[]},\"🚓\":{\"unicode\":\"🚓\",\"name\":\"police car\",\"variants\":[]},\"🚔\":{\"unicode\":\"🚔\",\"name\":\"oncoming police car\",\"variants\":[]},\"🚕\":{\"unicode\":\"🚕\",\"name\":\"taxi\",\"variants\":[]},\"🚖\":{\"unicode\":\"🚖\",\"name\":\"oncoming taxi\",\"variants\":[]},\"🚗\":{\"unicode\":\"🚗\",\"name\":\"automobile\",\"variants\":[]},\"🚘\":{\"unicode\":\"🚘\",\"name\":\"oncoming automobile\",\"variants\":[]},\"🚙\":{\"unicode\":\"🚙\",\"name\":\"sport utility vehicle\",\"variants\":[]},\"🛻\":{\"unicode\":\"🛻\",\"name\":\"pickup truck\",\"variants\":[]},\"🚚\":{\"unicode\":\"🚚\",\"name\":\"delivery truck\",\"variants\":[]},\"🚛\":{\"unicode\":\"🚛\",\"name\":\"articulated lorry\",\"variants\":[]},\"🚜\":{\"unicode\":\"🚜\",\"name\":\"tractor\",\"variants\":[]},\"🏎️\":{\"unicode\":\"🏎️\",\"name\":\"racing car\",\"variants\":[]},\"🏍️\":{\"unicode\":\"🏍️\",\"name\":\"motorcycle\",\"variants\":[]},\"🛵\":{\"unicode\":\"🛵\",\"name\":\"motor scooter\",\"variants\":[]},\"🦽\":{\"unicode\":\"🦽\",\"name\":\"manual wheelchair\",\"variants\":[]},\"🦼\":{\"unicode\":\"🦼\",\"name\":\"motorized wheelchair\",\"variants\":[]},\"🛺\":{\"unicode\":\"🛺\",\"name\":\"auto rickshaw\",\"variants\":[]},\"🚲\":{\"unicode\":\"🚲\",\"name\":\"bicycle\",\"variants\":[]},\"🛴\":{\"unicode\":\"🛴\",\"name\":\"kick scooter\",\"variants\":[]},\"🛹\":{\"unicode\":\"🛹\",\"name\":\"skateboard\",\"variants\":[]},\"🛼\":{\"unicode\":\"🛼\",\"name\":\"roller skate\",\"variants\":[]},\"🚏\":{\"unicode\":\"🚏\",\"name\":\"bus stop\",\"variants\":[]},\"🛣️\":{\"unicode\":\"🛣️\",\"name\":\"motorway\",\"variants\":[]},\"🛤️\":{\"unicode\":\"🛤️\",\"name\":\"railway track\",\"variants\":[]},\"🛢️\":{\"unicode\":\"🛢️\",\"name\":\"oil drum\",\"variants\":[]},\"⛽\":{\"unicode\":\"⛽\",\"name\":\"fuel pump\",\"variants\":[]},\"🚨\":{\"unicode\":\"🚨\",\"name\":\"police car light\",\"variants\":[]},\"🚥\":{\"unicode\":\"🚥\",\"name\":\"horizontal traffic light\",\"variants\":[]},\"🚦\":{\"unicode\":\"🚦\",\"name\":\"vertical traffic light\",\"variants\":[]},\"🛑\":{\"unicode\":\"🛑\",\"name\":\"stop sign\",\"variants\":[]},\"🚧\":{\"unicode\":\"🚧\",\"name\":\"construction\",\"variants\":[]},\"⚓\":{\"unicode\":\"⚓\",\"name\":\"anchor\",\"variants\":[]},\"⛵\":{\"unicode\":\"⛵\",\"name\":\"sailboat\",\"variants\":[]},\"🛶\":{\"unicode\":\"🛶\",\"name\":\"canoe\",\"variants\":[]},\"🚤\":{\"unicode\":\"🚤\",\"name\":\"speedboat\",\"variants\":[]},\"🛳️\":{\"unicode\":\"🛳️\",\"name\":\"passenger ship\",\"variants\":[]},\"⛴️\":{\"unicode\":\"⛴️\",\"name\":\"ferry\",\"variants\":[]},\"🛥️\":{\"unicode\":\"🛥️\",\"name\":\"motor boat\",\"variants\":[]},\"🚢\":{\"unicode\":\"🚢\",\"name\":\"ship\",\"variants\":[]},\"✈️\":{\"unicode\":\"✈️\",\"name\":\"airplane\",\"variants\":[]},\"🛩️\":{\"unicode\":\"🛩️\",\"name\":\"small airplane\",\"variants\":[]},\"🛫\":{\"unicode\":\"🛫\",\"name\":\"airplane departure\",\"variants\":[]},\"🛬\":{\"unicode\":\"🛬\",\"name\":\"airplane arrival\",\"variants\":[]},\"🪂\":{\"unicode\":\"🪂\",\"name\":\"parachute\",\"variants\":[]},\"💺\":{\"unicode\":\"💺\",\"name\":\"seat\",\"variants\":[]},\"🚁\":{\"unicode\":\"🚁\",\"name\":\"helicopter\",\"variants\":[]},\"🚟\":{\"unicode\":\"🚟\",\"name\":\"suspension railway\",\"variants\":[]},\"🚠\":{\"unicode\":\"🚠\",\"name\":\"mountain cableway\",\"variants\":[]},\"🚡\":{\"unicode\":\"🚡\",\"name\":\"aerial tramway\",\"variants\":[]},\"🛰️\":{\"unicode\":\"🛰️\",\"name\":\"satellite\",\"variants\":[]},\"🚀\":{\"unicode\":\"🚀\",\"name\":\"rocket\",\"variants\":[]},\"🛸\":{\"unicode\":\"🛸\",\"name\":\"flying saucer\",\"variants\":[]},\"🛎️\":{\"unicode\":\"🛎️\",\"name\":\"bellhop bell\",\"variants\":[]},\"🧳\":{\"unicode\":\"🧳\",\"name\":\"luggage\",\"variants\":[]},\"⌛\":{\"unicode\":\"⌛\",\"name\":\"hourglass done\",\"variants\":[]},\"⏳\":{\"unicode\":\"⏳\",\"name\":\"hourglass not done\",\"variants\":[]},\"⌚\":{\"unicode\":\"⌚\",\"name\":\"watch\",\"variants\":[]},\"⏰\":{\"unicode\":\"⏰\",\"name\":\"alarm clock\",\"variants\":[]},\"⏱️\":{\"unicode\":\"⏱️\",\"name\":\"stopwatch\",\"variants\":[]},\"⏲️\":{\"unicode\":\"⏲️\",\"name\":\"timer clock\",\"variants\":[]},\"🕰️\":{\"unicode\":\"🕰️\",\"name\":\"mantelpiece clock\",\"variants\":[]},\"🕛\":{\"unicode\":\"🕛\",\"name\":\"twelve oâ€™clock\",\"variants\":[]},\"🕧\":{\"unicode\":\"🕧\",\"name\":\"twelve-thirty\",\"variants\":[]},\"🕐\":{\"unicode\":\"🕐\",\"name\":\"one oâ€™clock\",\"variants\":[]},\"🕜\":{\"unicode\":\"🕜\",\"name\":\"one-thirty\",\"variants\":[]},\"🕑\":{\"unicode\":\"🕑\",\"name\":\"two oâ€™clock\",\"variants\":[]},\"🕝\":{\"unicode\":\"🕝\",\"name\":\"two-thirty\",\"variants\":[]},\"🕒\":{\"unicode\":\"🕒\",\"name\":\"three oâ€™clock\",\"variants\":[]},\"🕞\":{\"unicode\":\"🕞\",\"name\":\"three-thirty\",\"variants\":[]},\"🕓\":{\"unicode\":\"🕓\",\"name\":\"four oâ€™clock\",\"variants\":[]},\"🕟\":{\"unicode\":\"🕟\",\"name\":\"four-thirty\",\"variants\":[]},\"🕔\":{\"unicode\":\"🕔\",\"name\":\"five oâ€™clock\",\"variants\":[]},\"🕠\":{\"unicode\":\"🕠\",\"name\":\"five-thirty\",\"variants\":[]},\"🕕\":{\"unicode\":\"🕕\",\"name\":\"six oâ€™clock\",\"variants\":[]},\"🕡\":{\"unicode\":\"🕡\",\"name\":\"six-thirty\",\"variants\":[]},\"🕖\":{\"unicode\":\"🕖\",\"name\":\"seven oâ€™clock\",\"variants\":[]},\"🕢\":{\"unicode\":\"🕢\",\"name\":\"seven-thirty\",\"variants\":[]},\"🕗\":{\"unicode\":\"🕗\",\"name\":\"eight oâ€™clock\",\"variants\":[]},\"🕣\":{\"unicode\":\"🕣\",\"name\":\"eight-thirty\",\"variants\":[]},\"🕘\":{\"unicode\":\"🕘\",\"name\":\"nine oâ€™clock\",\"variants\":[]},\"🕤\":{\"unicode\":\"🕤\",\"name\":\"nine-thirty\",\"variants\":[]},\"🕙\":{\"unicode\":\"🕙\",\"name\":\"ten oâ€™clock\",\"variants\":[]},\"🕥\":{\"unicode\":\"🕥\",\"name\":\"ten-thirty\",\"variants\":[]},\"🕚\":{\"unicode\":\"🕚\",\"name\":\"eleven oâ€™clock\",\"variants\":[]},\"🕦\":{\"unicode\":\"🕦\",\"name\":\"eleven-thirty\",\"variants\":[]},\"🌑\":{\"unicode\":\"🌑\",\"name\":\"new moon\",\"variants\":[]},\"🌒\":{\"unicode\":\"🌒\",\"name\":\"waxing crescent moon\",\"variants\":[]},\"🌓\":{\"unicode\":\"🌓\",\"name\":\"first quarter moon\",\"variants\":[]},\"🌔\":{\"unicode\":\"🌔\",\"name\":\"waxing gibbous moon\",\"variants\":[]},\"🌕\":{\"unicode\":\"🌕\",\"name\":\"full moon\",\"variants\":[]},\"🌖\":{\"unicode\":\"🌖\",\"name\":\"waning gibbous moon\",\"variants\":[]},\"🌗\":{\"unicode\":\"🌗\",\"name\":\"last quarter moon\",\"variants\":[]},\"🌘\":{\"unicode\":\"🌘\",\"name\":\"waning crescent moon\",\"variants\":[]},\"🌙\":{\"unicode\":\"🌙\",\"name\":\"crescent moon\",\"variants\":[]},\"🌚\":{\"unicode\":\"🌚\",\"name\":\"new moon face\",\"variants\":[]},\"🌛\":{\"unicode\":\"🌛\",\"name\":\"first quarter moon face\",\"variants\":[]},\"🌜\":{\"unicode\":\"🌜\",\"name\":\"last quarter moon face\",\"variants\":[]},\"🌡️\":{\"unicode\":\"🌡️\",\"name\":\"thermometer\",\"variants\":[]},\"☀️\":{\"unicode\":\"☀️\",\"name\":\"sun\",\"variants\":[]},\"🌝\":{\"unicode\":\"🌝\",\"name\":\"full moon face\",\"variants\":[]},\"🌞\":{\"unicode\":\"🌞\",\"name\":\"sun with face\",\"variants\":[]},\"🪐\":{\"unicode\":\"🪐\",\"name\":\"ringed planet\",\"variants\":[]},\"⭐\":{\"unicode\":\"⭐\",\"name\":\"star\",\"variants\":[]},\"🌟\":{\"unicode\":\"🌟\",\"name\":\"glowing star\",\"variants\":[]},\"🌠\":{\"unicode\":\"🌠\",\"name\":\"shooting star\",\"variants\":[]},\"🌌\":{\"unicode\":\"🌌\",\"name\":\"milky way\",\"variants\":[]},\"☁️\":{\"unicode\":\"☁️\",\"name\":\"cloud\",\"variants\":[]},\"⛅\":{\"unicode\":\"⛅\",\"name\":\"sun behind cloud\",\"variants\":[]},\"⛈️\":{\"unicode\":\"⛈️\",\"name\":\"cloud with lightning and rain\",\"variants\":[]},\"🌤️\":{\"unicode\":\"🌤️\",\"name\":\"sun behind small cloud\",\"variants\":[]},\"🌥️\":{\"unicode\":\"🌥️\",\"name\":\"sun behind large cloud\",\"variants\":[]},\"🌦️\":{\"unicode\":\"🌦️\",\"name\":\"sun behind rain cloud\",\"variants\":[]},\"🌧️\":{\"unicode\":\"🌧️\",\"name\":\"cloud with rain\",\"variants\":[]},\"🌨️\":{\"unicode\":\"🌨️\",\"name\":\"cloud with snow\",\"variants\":[]},\"🌩️\":{\"unicode\":\"🌩️\",\"name\":\"cloud with lightning\",\"variants\":[]},\"🌪️\":{\"unicode\":\"🌪️\",\"name\":\"tornado\",\"variants\":[]},\"🌫️\":{\"unicode\":\"🌫️\",\"name\":\"fog\",\"variants\":[]},\"🌬️\":{\"unicode\":\"🌬️\",\"name\":\"wind face\",\"variants\":[]},\"🌀\":{\"unicode\":\"🌀\",\"name\":\"cyclone\",\"variants\":[]},\"🌈\":{\"unicode\":\"🌈\",\"name\":\"rainbow\",\"variants\":[]},\"🌂\":{\"unicode\":\"🌂\",\"name\":\"closed umbrella\",\"variants\":[]},\"☂️\":{\"unicode\":\"☂️\",\"name\":\"umbrella\",\"variants\":[]},\"☔\":{\"unicode\":\"☔\",\"name\":\"umbrella with rain drops\",\"variants\":[]},\"⛱️\":{\"unicode\":\"⛱️\",\"name\":\"umbrella on ground\",\"variants\":[]},\"⚡\":{\"unicode\":\"⚡\",\"name\":\"high voltage\",\"variants\":[]},\"❄️\":{\"unicode\":\"❄️\",\"name\":\"snowflake\",\"variants\":[]},\"☃️\":{\"unicode\":\"☃️\",\"name\":\"snowman\",\"variants\":[]},\"⛄\":{\"unicode\":\"⛄\",\"name\":\"snowman without snow\",\"variants\":[]},\"☄️\":{\"unicode\":\"☄️\",\"name\":\"comet\",\"variants\":[]},\"🔥\":{\"unicode\":\"🔥\",\"name\":\"fire\",\"variants\":[]},\"💧\":{\"unicode\":\"💧\",\"name\":\"droplet\",\"variants\":[]},\"🌊\":{\"unicode\":\"🌊\",\"name\":\"water wave\",\"variants\":[]}}},\"ACTIVITIES\":{\"type\":\"ACTIVITIES\",\"emojis\":{\"🎃\":{\"unicode\":\"🎃\",\"name\":\"jack-o-lantern\",\"variants\":[]},\"🎄\":{\"unicode\":\"🎄\",\"name\":\"Christmas tree\",\"variants\":[]},\"🎆\":{\"unicode\":\"🎆\",\"name\":\"fireworks\",\"variants\":[]},\"🎇\":{\"unicode\":\"🎇\",\"name\":\"sparkler\",\"variants\":[]},\"🧨\":{\"unicode\":\"🧨\",\"name\":\"firecracker\",\"variants\":[]},\"✨\":{\"unicode\":\"✨\",\"name\":\"sparkles\",\"variants\":[]},\"🎈\":{\"unicode\":\"🎈\",\"name\":\"balloon\",\"variants\":[]},\"🎉\":{\"unicode\":\"🎉\",\"name\":\"party popper\",\"variants\":[]},\"🎊\":{\"unicode\":\"🎊\",\"name\":\"confetti ball\",\"variants\":[]},\"🎋\":{\"unicode\":\"🎋\",\"name\":\"tanabata tree\",\"variants\":[]},\"🎍\":{\"unicode\":\"🎍\",\"name\":\"pine decoration\",\"variants\":[]},\"🎎\":{\"unicode\":\"🎎\",\"name\":\"Japanese dolls\",\"variants\":[]},\"🎏\":{\"unicode\":\"🎏\",\"name\":\"carp streamer\",\"variants\":[]},\"🎐\":{\"unicode\":\"🎐\",\"name\":\"wind chime\",\"variants\":[]},\"🎑\":{\"unicode\":\"🎑\",\"name\":\"moon viewing ceremony\",\"variants\":[]},\"🧧\":{\"unicode\":\"🧧\",\"name\":\"red envelope\",\"variants\":[]},\"🎀\":{\"unicode\":\"🎀\",\"name\":\"ribbon\",\"variants\":[]},\"🎁\":{\"unicode\":\"🎁\",\"name\":\"wrapped gift\",\"variants\":[]},\"🎗️\":{\"unicode\":\"🎗️\",\"name\":\"reminder ribbon\",\"variants\":[]},\"🎟️\":{\"unicode\":\"🎟️\",\"name\":\"admission tickets\",\"variants\":[]},\"🎫\":{\"unicode\":\"🎫\",\"name\":\"ticket\",\"variants\":[]},\"🎖️\":{\"unicode\":\"🎖️\",\"name\":\"military medal\",\"variants\":[]},\"🏆\":{\"unicode\":\"🏆\",\"name\":\"trophy\",\"variants\":[]},\"🏅\":{\"unicode\":\"🏅\",\"name\":\"sports medal\",\"variants\":[]},\"🥇\":{\"unicode\":\"🥇\",\"name\":\"1st place medal\",\"variants\":[]},\"🥈\":{\"unicode\":\"🥈\",\"name\":\"2nd place medal\",\"variants\":[]},\"🥉\":{\"unicode\":\"🥉\",\"name\":\"3rd place medal\",\"variants\":[]},\"⚽\":{\"unicode\":\"⚽\",\"name\":\"soccer ball\",\"variants\":[]},\"⚾\":{\"unicode\":\"⚾\",\"name\":\"baseball\",\"variants\":[]},\"🥎\":{\"unicode\":\"🥎\",\"name\":\"softball\",\"variants\":[]},\"🏀\":{\"unicode\":\"🏀\",\"name\":\"basketball\",\"variants\":[]},\"🏐\":{\"unicode\":\"🏐\",\"name\":\"volleyball\",\"variants\":[]},\"🏈\":{\"unicode\":\"🏈\",\"name\":\"american football\",\"variants\":[]},\"🏉\":{\"unicode\":\"🏉\",\"name\":\"rugby football\",\"variants\":[]},\"🎾\":{\"unicode\":\"🎾\",\"name\":\"tennis\",\"variants\":[]},\"🥏\":{\"unicode\":\"🥏\",\"name\":\"flying disc\",\"variants\":[]},\"🎳\":{\"unicode\":\"🎳\",\"name\":\"bowling\",\"variants\":[]},\"🏏\":{\"unicode\":\"🏏\",\"name\":\"cricket game\",\"variants\":[]},\"🏑\":{\"unicode\":\"🏑\",\"name\":\"field hockey\",\"variants\":[]},\"🏒\":{\"unicode\":\"🏒\",\"name\":\"ice hockey\",\"variants\":[]},\"🥍\":{\"unicode\":\"🥍\",\"name\":\"lacrosse\",\"variants\":[]},\"🏓\":{\"unicode\":\"🏓\",\"name\":\"ping pong\",\"variants\":[]},\"🏸\":{\"unicode\":\"🏸\",\"name\":\"badminton\",\"variants\":[]},\"🥊\":{\"unicode\":\"🥊\",\"name\":\"boxing glove\",\"variants\":[]},\"🥋\":{\"unicode\":\"🥋\",\"name\":\"martial arts uniform\",\"variants\":[]},\"🥅\":{\"unicode\":\"🥅\",\"name\":\"goal net\",\"variants\":[]},\"⛳\":{\"unicode\":\"⛳\",\"name\":\"flag in hole\",\"variants\":[]},\"⛸️\":{\"unicode\":\"⛸️\",\"name\":\"ice skate\",\"variants\":[]},\"🎣\":{\"unicode\":\"🎣\",\"name\":\"fishing pole\",\"variants\":[]},\"🤿\":{\"unicode\":\"🤿\",\"name\":\"diving mask\",\"variants\":[]},\"🎽\":{\"unicode\":\"🎽\",\"name\":\"running shirt\",\"variants\":[]},\"🎿\":{\"unicode\":\"🎿\",\"name\":\"skis\",\"variants\":[]},\"🛷\":{\"unicode\":\"🛷\",\"name\":\"sled\",\"variants\":[]},\"🥌\":{\"unicode\":\"🥌\",\"name\":\"curling stone\",\"variants\":[]},\"🎯\":{\"unicode\":\"🎯\",\"name\":\"direct hit\",\"variants\":[]},\"🪀\":{\"unicode\":\"🪀\",\"name\":\"yo-yo\",\"variants\":[]},\"🪁\":{\"unicode\":\"🪁\",\"name\":\"kite\",\"variants\":[]},\"🎱\":{\"unicode\":\"🎱\",\"name\":\"pool 8 ball\",\"variants\":[]},\"🔮\":{\"unicode\":\"🔮\",\"name\":\"crystal ball\",\"variants\":[]},\"🪄\":{\"unicode\":\"🪄\",\"name\":\"magic wand\",\"variants\":[]},\"🧿\":{\"unicode\":\"🧿\",\"name\":\"nazar amulet\",\"variants\":[]},\"🎮\":{\"unicode\":\"🎮\",\"name\":\"video game\",\"variants\":[]},\"🕹️\":{\"unicode\":\"🕹️\",\"name\":\"joystick\",\"variants\":[]},\"🎰\":{\"unicode\":\"🎰\",\"name\":\"slot machine\",\"variants\":[]},\"🎲\":{\"unicode\":\"🎲\",\"name\":\"game die\",\"variants\":[]},\"🧩\":{\"unicode\":\"🧩\",\"name\":\"puzzle piece\",\"variants\":[]},\"🧸\":{\"unicode\":\"🧸\",\"name\":\"teddy bear\",\"variants\":[]},\"🪅\":{\"unicode\":\"🪅\",\"name\":\"piÃ±ata\",\"variants\":[]},\"🪆\":{\"unicode\":\"🪆\",\"name\":\"nesting dolls\",\"variants\":[]},\"♠️\":{\"unicode\":\"♠️\",\"name\":\"spade suit\",\"variants\":[]},\"♥️\":{\"unicode\":\"♥️\",\"name\":\"heart suit\",\"variants\":[]},\"♦️\":{\"unicode\":\"♦️\",\"name\":\"diamond suit\",\"variants\":[]},\"♣️\":{\"unicode\":\"♣️\",\"name\":\"club suit\",\"variants\":[]},\"♟️\":{\"unicode\":\"♟️\",\"name\":\"chess pawn\",\"variants\":[]},\"🃏\":{\"unicode\":\"🃏\",\"name\":\"joker\",\"variants\":[]},\"🀄\":{\"unicode\":\"🀄\",\"name\":\"mahjong red dragon\",\"variants\":[]},\"🎴\":{\"unicode\":\"🎴\",\"name\":\"flower playing cards\",\"variants\":[]},\"🎭\":{\"unicode\":\"🎭\",\"name\":\"performing arts\",\"variants\":[]},\"🖼️\":{\"unicode\":\"🖼️\",\"name\":\"framed picture\",\"variants\":[]},\"🎨\":{\"unicode\":\"🎨\",\"name\":\"artist palette\",\"variants\":[]},\"🧵\":{\"unicode\":\"🧵\",\"name\":\"thread\",\"variants\":[]},\"🪡\":{\"unicode\":\"🪡\",\"name\":\"sewing needle\",\"variants\":[]},\"🧶\":{\"unicode\":\"🧶\",\"name\":\"yarn\",\"variants\":[]},\"🪢\":{\"unicode\":\"🪢\",\"name\":\"knot\",\"variants\":[]}}},\"OBJECTS\":{\"type\":\"OBJECTS\",\"emojis\":{\"👓\":{\"unicode\":\"👓\",\"name\":\"glasses\",\"variants\":[]},\"🕶️\":{\"unicode\":\"🕶️\",\"name\":\"sunglasses\",\"variants\":[]},\"🥽\":{\"unicode\":\"🥽\",\"name\":\"goggles\",\"variants\":[]},\"🥼\":{\"unicode\":\"🥼\",\"name\":\"lab coat\",\"variants\":[]},\"🦺\":{\"unicode\":\"🦺\",\"name\":\"safety vest\",\"variants\":[]},\"👔\":{\"unicode\":\"👔\",\"name\":\"necktie\",\"variants\":[]},\"👕\":{\"unicode\":\"👕\",\"name\":\"t-shirt\",\"variants\":[]},\"👖\":{\"unicode\":\"👖\",\"name\":\"jeans\",\"variants\":[]},\"🧣\":{\"unicode\":\"🧣\",\"name\":\"scarf\",\"variants\":[]},\"🧤\":{\"unicode\":\"🧤\",\"name\":\"gloves\",\"variants\":[]},\"🧥\":{\"unicode\":\"🧥\",\"name\":\"coat\",\"variants\":[]},\"🧦\":{\"unicode\":\"🧦\",\"name\":\"socks\",\"variants\":[]},\"👗\":{\"unicode\":\"👗\",\"name\":\"dress\",\"variants\":[]},\"👘\":{\"unicode\":\"👘\",\"name\":\"kimono\",\"variants\":[]},\"🥻\":{\"unicode\":\"🥻\",\"name\":\"sari\",\"variants\":[]},\"🩱\":{\"unicode\":\"🩱\",\"name\":\"one-piece swimsuit\",\"variants\":[]},\"🩲\":{\"unicode\":\"🩲\",\"name\":\"briefs\",\"variants\":[]},\"🩳\":{\"unicode\":\"🩳\",\"name\":\"shorts\",\"variants\":[]},\"👙\":{\"unicode\":\"👙\",\"name\":\"bikini\",\"variants\":[]},\"👚\":{\"unicode\":\"👚\",\"name\":\"womanâ€™s clothes\",\"variants\":[]},\"👛\":{\"unicode\":\"👛\",\"name\":\"purse\",\"variants\":[]},\"👜\":{\"unicode\":\"👜\",\"name\":\"handbag\",\"variants\":[]},\"👝\":{\"unicode\":\"👝\",\"name\":\"clutch bag\",\"variants\":[]},\"🛍️\":{\"unicode\":\"🛍️\",\"name\":\"shopping bags\",\"variants\":[]},\"🎒\":{\"unicode\":\"🎒\",\"name\":\"backpack\",\"variants\":[]},\"🩴\":{\"unicode\":\"🩴\",\"name\":\"thong sandal\",\"variants\":[]},\"👞\":{\"unicode\":\"👞\",\"name\":\"manâ€™s shoe\",\"variants\":[]},\"👟\":{\"unicode\":\"👟\",\"name\":\"running shoe\",\"variants\":[]},\"🥾\":{\"unicode\":\"🥾\",\"name\":\"hiking boot\",\"variants\":[]},\"🥿\":{\"unicode\":\"🥿\",\"name\":\"flat shoe\",\"variants\":[]},\"👠\":{\"unicode\":\"👠\",\"name\":\"high-heeled shoe\",\"variants\":[]},\"👡\":{\"unicode\":\"👡\",\"name\":\"womanâ€™s sandal\",\"variants\":[]},\"🩰\":{\"unicode\":\"🩰\",\"name\":\"ballet shoes\",\"variants\":[]},\"👢\":{\"unicode\":\"👢\",\"name\":\"womanâ€™s boot\",\"variants\":[]},\"👑\":{\"unicode\":\"👑\",\"name\":\"crown\",\"variants\":[]},\"👒\":{\"unicode\":\"👒\",\"name\":\"womanâ€™s hat\",\"variants\":[]},\"🎩\":{\"unicode\":\"🎩\",\"name\":\"top hat\",\"variants\":[]},\"🎓\":{\"unicode\":\"🎓\",\"name\":\"graduation cap\",\"variants\":[]},\"🧢\":{\"unicode\":\"🧢\",\"name\":\"billed cap\",\"variants\":[]},\"🪖\":{\"unicode\":\"🪖\",\"name\":\"military helmet\",\"variants\":[]},\"⛑️\":{\"unicode\":\"⛑️\",\"name\":\"rescue workerâ€™s helmet\",\"variants\":[]},\"📿\":{\"unicode\":\"📿\",\"name\":\"prayer beads\",\"variants\":[]},\"💄\":{\"unicode\":\"💄\",\"name\":\"lipstick\",\"variants\":[]},\"💍\":{\"unicode\":\"💍\",\"name\":\"ring\",\"variants\":[]},\"💎\":{\"unicode\":\"💎\",\"name\":\"gem stone\",\"variants\":[]},\"🔇\":{\"unicode\":\"🔇\",\"name\":\"muted speaker\",\"variants\":[]},\"🔈\":{\"unicode\":\"🔈\",\"name\":\"speaker low volume\",\"variants\":[]},\"🔉\":{\"unicode\":\"🔉\",\"name\":\"speaker medium volume\",\"variants\":[]},\"🔊\":{\"unicode\":\"🔊\",\"name\":\"speaker high volume\",\"variants\":[]},\"📢\":{\"unicode\":\"📢\",\"name\":\"loudspeaker\",\"variants\":[]},\"📣\":{\"unicode\":\"📣\",\"name\":\"megaphone\",\"variants\":[]},\"📯\":{\"unicode\":\"📯\",\"name\":\"postal horn\",\"variants\":[]},\"🔔\":{\"unicode\":\"🔔\",\"name\":\"bell\",\"variants\":[]},\"🔕\":{\"unicode\":\"🔕\",\"name\":\"bell with slash\",\"variants\":[]},\"🎼\":{\"unicode\":\"🎼\",\"name\":\"musical score\",\"variants\":[]},\"🎵\":{\"unicode\":\"🎵\",\"name\":\"musical note\",\"variants\":[]},\"🎶\":{\"unicode\":\"🎶\",\"name\":\"musical notes\",\"variants\":[]},\"🎙️\":{\"unicode\":\"🎙️\",\"name\":\"studio microphone\",\"variants\":[]},\"🎚️\":{\"unicode\":\"🎚️\",\"name\":\"level slider\",\"variants\":[]},\"🎛️\":{\"unicode\":\"🎛️\",\"name\":\"control knobs\",\"variants\":[]},\"🎤\":{\"unicode\":\"🎤\",\"name\":\"microphone\",\"variants\":[]},\"🎧\":{\"unicode\":\"🎧\",\"name\":\"headphone\",\"variants\":[]},\"📻\":{\"unicode\":\"📻\",\"name\":\"radio\",\"variants\":[]},\"🎷\":{\"unicode\":\"🎷\",\"name\":\"saxophone\",\"variants\":[]},\"🪗\":{\"unicode\":\"🪗\",\"name\":\"accordion\",\"variants\":[]},\"🎸\":{\"unicode\":\"🎸\",\"name\":\"guitar\",\"variants\":[]},\"🎹\":{\"unicode\":\"🎹\",\"name\":\"musical keyboard\",\"variants\":[]},\"🎺\":{\"unicode\":\"🎺\",\"name\":\"trumpet\",\"variants\":[]},\"🎻\":{\"unicode\":\"🎻\",\"name\":\"violin\",\"variants\":[]},\"🪕\":{\"unicode\":\"🪕\",\"name\":\"banjo\",\"variants\":[]},\"🥁\":{\"unicode\":\"🥁\",\"name\":\"drum\",\"variants\":[]},\"🪘\":{\"unicode\":\"🪘\",\"name\":\"long drum\",\"variants\":[]},\"📱\":{\"unicode\":\"📱\",\"name\":\"mobile phone\",\"variants\":[]},\"📲\":{\"unicode\":\"📲\",\"name\":\"mobile phone with arrow\",\"variants\":[]},\"☎️\":{\"unicode\":\"☎️\",\"name\":\"telephone\",\"variants\":[]},\"📞\":{\"unicode\":\"📞\",\"name\":\"telephone receiver\",\"variants\":[]},\"📟\":{\"unicode\":\"📟\",\"name\":\"pager\",\"variants\":[]},\"📠\":{\"unicode\":\"📠\",\"name\":\"fax machine\",\"variants\":[]},\"🔋\":{\"unicode\":\"🔋\",\"name\":\"battery\",\"variants\":[]},\"🔌\":{\"unicode\":\"🔌\",\"name\":\"electric plug\",\"variants\":[]},\"💻\":{\"unicode\":\"💻\",\"name\":\"laptop\",\"variants\":[]},\"🖥️\":{\"unicode\":\"🖥️\",\"name\":\"desktop computer\",\"variants\":[]},\"🖨️\":{\"unicode\":\"🖨️\",\"name\":\"printer\",\"variants\":[]},\"⌨️\":{\"unicode\":\"⌨️\",\"name\":\"keyboard\",\"variants\":[]},\"🖱️\":{\"unicode\":\"🖱️\",\"name\":\"computer mouse\",\"variants\":[]},\"🖲️\":{\"unicode\":\"🖲️\",\"name\":\"trackball\",\"variants\":[]},\"💽\":{\"unicode\":\"💽\",\"name\":\"computer disk\",\"variants\":[]},\"💾\":{\"unicode\":\"💾\",\"name\":\"floppy disk\",\"variants\":[]},\"💿\":{\"unicode\":\"💿\",\"name\":\"optical disk\",\"variants\":[]},\"📀\":{\"unicode\":\"📀\",\"name\":\"dvd\",\"variants\":[]},\"🧮\":{\"unicode\":\"🧮\",\"name\":\"abacus\",\"variants\":[]},\"🎥\":{\"unicode\":\"🎥\",\"name\":\"movie camera\",\"variants\":[]},\"🎞️\":{\"unicode\":\"🎞️\",\"name\":\"film frames\",\"variants\":[]},\"📽️\":{\"unicode\":\"📽️\",\"name\":\"film projector\",\"variants\":[]},\"🎬\":{\"unicode\":\"🎬\",\"name\":\"clapper board\",\"variants\":[]},\"📺\":{\"unicode\":\"📺\",\"name\":\"television\",\"variants\":[]},\"📷\":{\"unicode\":\"📷\",\"name\":\"camera\",\"variants\":[]},\"📸\":{\"unicode\":\"📸\",\"name\":\"camera with flash\",\"variants\":[]},\"📹\":{\"unicode\":\"📹\",\"name\":\"video camera\",\"variants\":[]},\"📼\":{\"unicode\":\"📼\",\"name\":\"videocassette\",\"variants\":[]},\"🔍\":{\"unicode\":\"🔍\",\"name\":\"magnifying glass tilted left\",\"variants\":[]},\"🔎\":{\"unicode\":\"🔎\",\"name\":\"magnifying glass tilted right\",\"variants\":[]},\"🕯️\":{\"unicode\":\"🕯️\",\"name\":\"candle\",\"variants\":[]},\"💡\":{\"unicode\":\"💡\",\"name\":\"light bulb\",\"variants\":[]},\"🔦\":{\"unicode\":\"🔦\",\"name\":\"flashlight\",\"variants\":[]},\"🏮\":{\"unicode\":\"🏮\",\"name\":\"red paper lantern\",\"variants\":[]},\"🪔\":{\"unicode\":\"🪔\",\"name\":\"diya lamp\",\"variants\":[]},\"📔\":{\"unicode\":\"📔\",\"name\":\"notebook with decorative cover\",\"variants\":[]},\"📕\":{\"unicode\":\"📕\",\"name\":\"closed book\",\"variants\":[]},\"📖\":{\"unicode\":\"📖\",\"name\":\"open book\",\"variants\":[]},\"📗\":{\"unicode\":\"📗\",\"name\":\"green book\",\"variants\":[]},\"📘\":{\"unicode\":\"📘\",\"name\":\"blue book\",\"variants\":[]},\"📙\":{\"unicode\":\"📙\",\"name\":\"orange book\",\"variants\":[]},\"📚\":{\"unicode\":\"📚\",\"name\":\"books\",\"variants\":[]},\"📓\":{\"unicode\":\"📓\",\"name\":\"notebook\",\"variants\":[]},\"📒\":{\"unicode\":\"📒\",\"name\":\"ledger\",\"variants\":[]},\"📃\":{\"unicode\":\"📃\",\"name\":\"page with curl\",\"variants\":[]},\"📜\":{\"unicode\":\"📜\",\"name\":\"scroll\",\"variants\":[]},\"📄\":{\"unicode\":\"📄\",\"name\":\"page facing up\",\"variants\":[]},\"📰\":{\"unicode\":\"📰\",\"name\":\"newspaper\",\"variants\":[]},\"🗞️\":{\"unicode\":\"🗞️\",\"name\":\"rolled-up newspaper\",\"variants\":[]},\"📑\":{\"unicode\":\"📑\",\"name\":\"bookmark tabs\",\"variants\":[]},\"🔖\":{\"unicode\":\"🔖\",\"name\":\"bookmark\",\"variants\":[]},\"🏷️\":{\"unicode\":\"🏷️\",\"name\":\"label\",\"variants\":[]},\"💰\":{\"unicode\":\"💰\",\"name\":\"money bag\",\"variants\":[]},\"🪙\":{\"unicode\":\"🪙\",\"name\":\"coin\",\"variants\":[]},\"💴\":{\"unicode\":\"💴\",\"name\":\"yen banknote\",\"variants\":[]},\"💵\":{\"unicode\":\"💵\",\"name\":\"dollar banknote\",\"variants\":[]},\"💶\":{\"unicode\":\"💶\",\"name\":\"euro banknote\",\"variants\":[]},\"💷\":{\"unicode\":\"💷\",\"name\":\"pound banknote\",\"variants\":[]},\"💸\":{\"unicode\":\"💸\",\"name\":\"money with wings\",\"variants\":[]},\"💳\":{\"unicode\":\"💳\",\"name\":\"credit card\",\"variants\":[]},\"🧾\":{\"unicode\":\"🧾\",\"name\":\"receipt\",\"variants\":[]},\"💹\":{\"unicode\":\"💹\",\"name\":\"chart increasing with yen\",\"variants\":[]},\"✉️\":{\"unicode\":\"✉️\",\"name\":\"envelope\",\"variants\":[]},\"📧\":{\"unicode\":\"📧\",\"name\":\"e-mail\",\"variants\":[]},\"📨\":{\"unicode\":\"📨\",\"name\":\"incoming envelope\",\"variants\":[]},\"📩\":{\"unicode\":\"📩\",\"name\":\"envelope with arrow\",\"variants\":[]},\"📤\":{\"unicode\":\"📤\",\"name\":\"outbox tray\",\"variants\":[]},\"📥\":{\"unicode\":\"📥\",\"name\":\"inbox tray\",\"variants\":[]},\"📦\":{\"unicode\":\"📦\",\"name\":\"package\",\"variants\":[]},\"📫\":{\"unicode\":\"📫\",\"name\":\"closed mailbox with raised flag\",\"variants\":[]},\"📪\":{\"unicode\":\"📪\",\"name\":\"closed mailbox with lowered flag\",\"variants\":[]},\"📬\":{\"unicode\":\"📬\",\"name\":\"open mailbox with raised flag\",\"variants\":[]},\"📭\":{\"unicode\":\"📭\",\"name\":\"open mailbox with lowered flag\",\"variants\":[]},\"📮\":{\"unicode\":\"📮\",\"name\":\"postbox\",\"variants\":[]},\"🗳️\":{\"unicode\":\"🗳️\",\"name\":\"ballot box with ballot\",\"variants\":[]},\"✏️\":{\"unicode\":\"✏️\",\"name\":\"pencil\",\"variants\":[]},\"✒️\":{\"unicode\":\"✒️\",\"name\":\"black nib\",\"variants\":[]},\"🖋️\":{\"unicode\":\"🖋️\",\"name\":\"fountain pen\",\"variants\":[]},\"🖊️\":{\"unicode\":\"🖊️\",\"name\":\"pen\",\"variants\":[]},\"🖌️\":{\"unicode\":\"🖌️\",\"name\":\"paintbrush\",\"variants\":[]},\"🖍️\":{\"unicode\":\"🖍️\",\"name\":\"crayon\",\"variants\":[]},\"📝\":{\"unicode\":\"📝\",\"name\":\"memo\",\"variants\":[]},\"💼\":{\"unicode\":\"💼\",\"name\":\"briefcase\",\"variants\":[]},\"📁\":{\"unicode\":\"📁\",\"name\":\"file folder\",\"variants\":[]},\"📂\":{\"unicode\":\"📂\",\"name\":\"open file folder\",\"variants\":[]},\"🗂️\":{\"unicode\":\"🗂️\",\"name\":\"card index dividers\",\"variants\":[]},\"📅\":{\"unicode\":\"📅\",\"name\":\"calendar\",\"variants\":[]},\"📆\":{\"unicode\":\"📆\",\"name\":\"tear-off calendar\",\"variants\":[]},\"🗒️\":{\"unicode\":\"🗒️\",\"name\":\"spiral notepad\",\"variants\":[]},\"🗓️\":{\"unicode\":\"🗓️\",\"name\":\"spiral calendar\",\"variants\":[]},\"📇\":{\"unicode\":\"📇\",\"name\":\"card index\",\"variants\":[]},\"📈\":{\"unicode\":\"📈\",\"name\":\"chart increasing\",\"variants\":[]},\"📉\":{\"unicode\":\"📉\",\"name\":\"chart decreasing\",\"variants\":[]},\"📊\":{\"unicode\":\"📊\",\"name\":\"bar chart\",\"variants\":[]},\"📋\":{\"unicode\":\"📋\",\"name\":\"clipboard\",\"variants\":[]},\"📌\":{\"unicode\":\"📌\",\"name\":\"pushpin\",\"variants\":[]},\"📍\":{\"unicode\":\"📍\",\"name\":\"round pushpin\",\"variants\":[]},\"📎\":{\"unicode\":\"📎\",\"name\":\"paperclip\",\"variants\":[]},\"🖇️\":{\"unicode\":\"🖇️\",\"name\":\"linked paperclips\",\"variants\":[]},\"📏\":{\"unicode\":\"📏\",\"name\":\"straight ruler\",\"variants\":[]},\"📐\":{\"unicode\":\"📐\",\"name\":\"triangular ruler\",\"variants\":[]},\"✂️\":{\"unicode\":\"✂️\",\"name\":\"scissors\",\"variants\":[]},\"🗃️\":{\"unicode\":\"🗃️\",\"name\":\"card file box\",\"variants\":[]},\"🗄️\":{\"unicode\":\"🗄️\",\"name\":\"file cabinet\",\"variants\":[]},\"🗑️\":{\"unicode\":\"🗑️\",\"name\":\"wastebasket\",\"variants\":[]},\"🔒\":{\"unicode\":\"🔒\",\"name\":\"locked\",\"variants\":[]},\"🔓\":{\"unicode\":\"🔓\",\"name\":\"unlocked\",\"variants\":[]},\"🔏\":{\"unicode\":\"🔏\",\"name\":\"locked with pen\",\"variants\":[]},\"🔐\":{\"unicode\":\"🔐\",\"name\":\"locked with key\",\"variants\":[]},\"🔑\":{\"unicode\":\"🔑\",\"name\":\"key\",\"variants\":[]},\"🗝️\":{\"unicode\":\"🗝️\",\"name\":\"old key\",\"variants\":[]},\"🔨\":{\"unicode\":\"🔨\",\"name\":\"hammer\",\"variants\":[]},\"🪓\":{\"unicode\":\"🪓\",\"name\":\"axe\",\"variants\":[]},\"⛏️\":{\"unicode\":\"⛏️\",\"name\":\"pick\",\"variants\":[]},\"⚒️\":{\"unicode\":\"⚒️\",\"name\":\"hammer and pick\",\"variants\":[]},\"🛠️\":{\"unicode\":\"🛠️\",\"name\":\"hammer and wrench\",\"variants\":[]},\"🗡️\":{\"unicode\":\"🗡️\",\"name\":\"dagger\",\"variants\":[]},\"⚔️\":{\"unicode\":\"⚔️\",\"name\":\"crossed swords\",\"variants\":[]},\"🔫\":{\"unicode\":\"🔫\",\"name\":\"pistol\",\"variants\":[]},\"🪃\":{\"unicode\":\"🪃\",\"name\":\"boomerang\",\"variants\":[]},\"🏹\":{\"unicode\":\"🏹\",\"name\":\"bow and arrow\",\"variants\":[]},\"🛡️\":{\"unicode\":\"🛡️\",\"name\":\"shield\",\"variants\":[]},\"🪚\":{\"unicode\":\"🪚\",\"name\":\"carpentry saw\",\"variants\":[]},\"🔧\":{\"unicode\":\"🔧\",\"name\":\"wrench\",\"variants\":[]},\"🪛\":{\"unicode\":\"🪛\",\"name\":\"screwdriver\",\"variants\":[]},\"🔩\":{\"unicode\":\"🔩\",\"name\":\"nut and bolt\",\"variants\":[]},\"⚙️\":{\"unicode\":\"⚙️\",\"name\":\"gear\",\"variants\":[]},\"🗜️\":{\"unicode\":\"🗜️\",\"name\":\"clamp\",\"variants\":[]},\"⚖️\":{\"unicode\":\"⚖️\",\"name\":\"balance scale\",\"variants\":[]},\"🦯\":{\"unicode\":\"🦯\",\"name\":\"white cane\",\"variants\":[]},\"🔗\":{\"unicode\":\"🔗\",\"name\":\"link\",\"variants\":[]},\"⛓️\":{\"unicode\":\"⛓️\",\"name\":\"chains\",\"variants\":[]},\"🪝\":{\"unicode\":\"🪝\",\"name\":\"hook\",\"variants\":[]},\"🧰\":{\"unicode\":\"🧰\",\"name\":\"toolbox\",\"variants\":[]},\"🧲\":{\"unicode\":\"🧲\",\"name\":\"magnet\",\"variants\":[]},\"🪜\":{\"unicode\":\"🪜\",\"name\":\"ladder\",\"variants\":[]},\"⚗️\":{\"unicode\":\"⚗️\",\"name\":\"alembic\",\"variants\":[]},\"🧪\":{\"unicode\":\"🧪\",\"name\":\"test tube\",\"variants\":[]},\"🧫\":{\"unicode\":\"🧫\",\"name\":\"petri dish\",\"variants\":[]},\"🧬\":{\"unicode\":\"🧬\",\"name\":\"dna\",\"variants\":[]},\"🔬\":{\"unicode\":\"🔬\",\"name\":\"microscope\",\"variants\":[]},\"🔭\":{\"unicode\":\"🔭\",\"name\":\"telescope\",\"variants\":[]},\"📡\":{\"unicode\":\"📡\",\"name\":\"satellite antenna\",\"variants\":[]},\"💉\":{\"unicode\":\"💉\",\"name\":\"syringe\",\"variants\":[]},\"🩸\":{\"unicode\":\"🩸\",\"name\":\"drop of blood\",\"variants\":[]},\"💊\":{\"unicode\":\"💊\",\"name\":\"pill\",\"variants\":[]},\"🩹\":{\"unicode\":\"🩹\",\"name\":\"adhesive bandage\",\"variants\":[]},\"🩺\":{\"unicode\":\"🩺\",\"name\":\"stethoscope\",\"variants\":[]},\"🚪\":{\"unicode\":\"🚪\",\"name\":\"door\",\"variants\":[]},\"🛗\":{\"unicode\":\"🛗\",\"name\":\"elevator\",\"variants\":[]},\"🪞\":{\"unicode\":\"🪞\",\"name\":\"mirror\",\"variants\":[]},\"🪟\":{\"unicode\":\"🪟\",\"name\":\"window\",\"variants\":[]},\"🛏️\":{\"unicode\":\"🛏️\",\"name\":\"bed\",\"variants\":[]},\"🛋️\":{\"unicode\":\"🛋️\",\"name\":\"couch and lamp\",\"variants\":[]},\"🪑\":{\"unicode\":\"🪑\",\"name\":\"chair\",\"variants\":[]},\"🚽\":{\"unicode\":\"🚽\",\"name\":\"toilet\",\"variants\":[]},\"🪠\":{\"unicode\":\"🪠\",\"name\":\"plunger\",\"variants\":[]},\"🚿\":{\"unicode\":\"🚿\",\"name\":\"shower\",\"variants\":[]},\"🛁\":{\"unicode\":\"🛁\",\"name\":\"bathtub\",\"variants\":[]},\"🪤\":{\"unicode\":\"🪤\",\"name\":\"mouse trap\",\"variants\":[]},\"🪒\":{\"unicode\":\"🪒\",\"name\":\"razor\",\"variants\":[]},\"🧴\":{\"unicode\":\"🧴\",\"name\":\"lotion bottle\",\"variants\":[]},\"🧷\":{\"unicode\":\"🧷\",\"name\":\"safety pin\",\"variants\":[]},\"🧹\":{\"unicode\":\"🧹\",\"name\":\"broom\",\"variants\":[]},\"🧺\":{\"unicode\":\"🧺\",\"name\":\"basket\",\"variants\":[]},\"🧻\":{\"unicode\":\"🧻\",\"name\":\"roll of paper\",\"variants\":[]},\"🪣\":{\"unicode\":\"🪣\",\"name\":\"bucket\",\"variants\":[]},\"🧼\":{\"unicode\":\"🧼\",\"name\":\"soap\",\"variants\":[]},\"🪥\":{\"unicode\":\"🪥\",\"name\":\"toothbrush\",\"variants\":[]},\"🧽\":{\"unicode\":\"🧽\",\"name\":\"sponge\",\"variants\":[]},\"🧯\":{\"unicode\":\"🧯\",\"name\":\"fire extinguisher\",\"variants\":[]},\"🛒\":{\"unicode\":\"🛒\",\"name\":\"shopping cart\",\"variants\":[]},\"🚬\":{\"unicode\":\"🚬\",\"name\":\"cigarette\",\"variants\":[]},\"⚰️\":{\"unicode\":\"⚰️\",\"name\":\"coffin\",\"variants\":[]},\"🪦\":{\"unicode\":\"🪦\",\"name\":\"headstone\",\"variants\":[]},\"⚱️\":{\"unicode\":\"⚱️\",\"name\":\"funeral urn\",\"variants\":[]},\"🗿\":{\"unicode\":\"🗿\",\"name\":\"moai\",\"variants\":[]},\"🪧\":{\"unicode\":\"🪧\",\"name\":\"placard\",\"variants\":[]}}},\"SYMBOLS\":{\"type\":\"SYMBOLS\",\"emojis\":{\"🏧\":{\"unicode\":\"🏧\",\"name\":\"ATM sign\",\"variants\":[]},\"🚮\":{\"unicode\":\"🚮\",\"name\":\"litter in bin sign\",\"variants\":[]},\"🚰\":{\"unicode\":\"🚰\",\"name\":\"potable water\",\"variants\":[]},\"♿\":{\"unicode\":\"♿\",\"name\":\"wheelchair symbol\",\"variants\":[]},\"🚹\":{\"unicode\":\"🚹\",\"name\":\"menâ€™s room\",\"variants\":[]},\"🚺\":{\"unicode\":\"🚺\",\"name\":\"womenâ€™s room\",\"variants\":[]},\"🚻\":{\"unicode\":\"🚻\",\"name\":\"restroom\",\"variants\":[]},\"🚼\":{\"unicode\":\"🚼\",\"name\":\"baby symbol\",\"variants\":[]},\"🚾\":{\"unicode\":\"🚾\",\"name\":\"water closet\",\"variants\":[]},\"🛂\":{\"unicode\":\"🛂\",\"name\":\"passport control\",\"variants\":[]},\"🛃\":{\"unicode\":\"🛃\",\"name\":\"customs\",\"variants\":[]},\"🛄\":{\"unicode\":\"🛄\",\"name\":\"baggage claim\",\"variants\":[]},\"🛅\":{\"unicode\":\"🛅\",\"name\":\"left luggage\",\"variants\":[]},\"⚠️\":{\"unicode\":\"⚠️\",\"name\":\"warning\",\"variants\":[]},\"🚸\":{\"unicode\":\"🚸\",\"name\":\"children crossing\",\"variants\":[]},\"⛔\":{\"unicode\":\"⛔\",\"name\":\"no entry\",\"variants\":[]},\"🚫\":{\"unicode\":\"🚫\",\"name\":\"prohibited\",\"variants\":[]},\"🚳\":{\"unicode\":\"🚳\",\"name\":\"no bicycles\",\"variants\":[]},\"🚭\":{\"unicode\":\"🚭\",\"name\":\"no smoking\",\"variants\":[]},\"🚯\":{\"unicode\":\"🚯\",\"name\":\"no littering\",\"variants\":[]},\"🚱\":{\"unicode\":\"🚱\",\"name\":\"non-potable water\",\"variants\":[]},\"🚷\":{\"unicode\":\"🚷\",\"name\":\"no pedestrians\",\"variants\":[]},\"📵\":{\"unicode\":\"📵\",\"name\":\"no mobile phones\",\"variants\":[]},\"🔞\":{\"unicode\":\"🔞\",\"name\":\"no one under eighteen\",\"variants\":[]},\"☢️\":{\"unicode\":\"☢️\",\"name\":\"radioactive\",\"variants\":[]},\"☣️\":{\"unicode\":\"☣️\",\"name\":\"biohazard\",\"variants\":[]},\"⬆️\":{\"unicode\":\"⬆️\",\"name\":\"up arrow\",\"variants\":[]},\"↗️\":{\"unicode\":\"↗️\",\"name\":\"up-right arrow\",\"variants\":[]},\"➡️\":{\"unicode\":\"➡️\",\"name\":\"right arrow\",\"variants\":[]},\"↘️\":{\"unicode\":\"↘️\",\"name\":\"down-right arrow\",\"variants\":[]},\"⬇️\":{\"unicode\":\"⬇️\",\"name\":\"down arrow\",\"variants\":[]},\"↙️\":{\"unicode\":\"↙️\",\"name\":\"down-left arrow\",\"variants\":[]},\"⬅️\":{\"unicode\":\"⬅️\",\"name\":\"left arrow\",\"variants\":[]},\"↖️\":{\"unicode\":\"↖️\",\"name\":\"up-left arrow\",\"variants\":[]},\"↕️\":{\"unicode\":\"↕️\",\"name\":\"up-down arrow\",\"variants\":[]},\"↔️\":{\"unicode\":\"↔️\",\"name\":\"left-right arrow\",\"variants\":[]},\"↩️\":{\"unicode\":\"↩️\",\"name\":\"right arrow curving left\",\"variants\":[]},\"↪️\":{\"unicode\":\"↪️\",\"name\":\"left arrow curving right\",\"variants\":[]},\"⤴️\":{\"unicode\":\"⤴️\",\"name\":\"right arrow curving up\",\"variants\":[]},\"⤵️\":{\"unicode\":\"⤵️\",\"name\":\"right arrow curving down\",\"variants\":[]},\"🔃\":{\"unicode\":\"🔃\",\"name\":\"clockwise vertical arrows\",\"variants\":[]},\"🔄\":{\"unicode\":\"🔄\",\"name\":\"counterclockwise arrows button\",\"variants\":[]},\"🔙\":{\"unicode\":\"🔙\",\"name\":\"BACK arrow\",\"variants\":[]},\"🔚\":{\"unicode\":\"🔚\",\"name\":\"END arrow\",\"variants\":[]},\"🔛\":{\"unicode\":\"🔛\",\"name\":\"ON! arrow\",\"variants\":[]},\"🔜\":{\"unicode\":\"🔜\",\"name\":\"SOON arrow\",\"variants\":[]},\"🔝\":{\"unicode\":\"🔝\",\"name\":\"TOP arrow\",\"variants\":[]},\"🛐\":{\"unicode\":\"🛐\",\"name\":\"place of worship\",\"variants\":[]},\"⚛️\":{\"unicode\":\"⚛️\",\"name\":\"atom symbol\",\"variants\":[]},\"🕉️\":{\"unicode\":\"🕉️\",\"name\":\"om\",\"variants\":[]},\"✡️\":{\"unicode\":\"✡️\",\"name\":\"star of David\",\"variants\":[]},\"☸️\":{\"unicode\":\"☸️\",\"name\":\"wheel of dharma\",\"variants\":[]},\"☯️\":{\"unicode\":\"☯️\",\"name\":\"yin yang\",\"variants\":[]},\"✝️\":{\"unicode\":\"✝️\",\"name\":\"latin cross\",\"variants\":[]},\"☦️\":{\"unicode\":\"☦️\",\"name\":\"orthodox cross\",\"variants\":[]},\"☪️\":{\"unicode\":\"☪️\",\"name\":\"star and crescent\",\"variants\":[]},\"☮️\":{\"unicode\":\"☮️\",\"name\":\"peace symbol\",\"variants\":[]},\"🕎\":{\"unicode\":\"🕎\",\"name\":\"menorah\",\"variants\":[]},\"🔯\":{\"unicode\":\"🔯\",\"name\":\"dotted six-pointed star\",\"variants\":[]},\"♈\":{\"unicode\":\"♈\",\"name\":\"Aries\",\"variants\":[]},\"♉\":{\"unicode\":\"♉\",\"name\":\"Taurus\",\"variants\":[]},\"♊\":{\"unicode\":\"♊\",\"name\":\"Gemini\",\"variants\":[]},\"♋\":{\"unicode\":\"♋\",\"name\":\"Cancer\",\"variants\":[]},\"♌\":{\"unicode\":\"♌\",\"name\":\"Leo\",\"variants\":[]},\"♍\":{\"unicode\":\"♍\",\"name\":\"Virgo\",\"variants\":[]},\"♎\":{\"unicode\":\"♎\",\"name\":\"Libra\",\"variants\":[]},\"♏\":{\"unicode\":\"♏\",\"name\":\"Scorpio\",\"variants\":[]},\"♐\":{\"unicode\":\"♐\",\"name\":\"Sagittarius\",\"variants\":[]},\"♑\":{\"unicode\":\"♑\",\"name\":\"Capricorn\",\"variants\":[]},\"♒\":{\"unicode\":\"♒\",\"name\":\"Aquarius\",\"variants\":[]},\"♓\":{\"unicode\":\"♓\",\"name\":\"Pisces\",\"variants\":[]},\"⛎\":{\"unicode\":\"⛎\",\"name\":\"Ophiuchus\",\"variants\":[]},\"🔀\":{\"unicode\":\"🔀\",\"name\":\"shuffle tracks button\",\"variants\":[]},\"🔁\":{\"unicode\":\"🔁\",\"name\":\"repeat button\",\"variants\":[]},\"🔂\":{\"unicode\":\"🔂\",\"name\":\"repeat single button\",\"variants\":[]},\"▶️\":{\"unicode\":\"▶️\",\"name\":\"play button\",\"variants\":[]},\"⏩\":{\"unicode\":\"⏩\",\"name\":\"fast-forward button\",\"variants\":[]},\"⏭️\":{\"unicode\":\"⏭️\",\"name\":\"next track button\",\"variants\":[]},\"⏯️\":{\"unicode\":\"⏯️\",\"name\":\"play or pause button\",\"variants\":[]},\"◀️\":{\"unicode\":\"◀️\",\"name\":\"reverse button\",\"variants\":[]},\"⏪\":{\"unicode\":\"⏪\",\"name\":\"fast reverse button\",\"variants\":[]},\"⏮️\":{\"unicode\":\"⏮️\",\"name\":\"last track button\",\"variants\":[]},\"🔼\":{\"unicode\":\"🔼\",\"name\":\"upwards button\",\"variants\":[]},\"⏫\":{\"unicode\":\"⏫\",\"name\":\"fast up button\",\"variants\":[]},\"🔽\":{\"unicode\":\"🔽\",\"name\":\"downwards button\",\"variants\":[]},\"⏬\":{\"unicode\":\"⏬\",\"name\":\"fast down button\",\"variants\":[]},\"⏸️\":{\"unicode\":\"⏸️\",\"name\":\"pause button\",\"variants\":[]},\"⏹️\":{\"unicode\":\"⏹️\",\"name\":\"stop button\",\"variants\":[]},\"⏺️\":{\"unicode\":\"⏺️\",\"name\":\"record button\",\"variants\":[]},\"⏏️\":{\"unicode\":\"⏏️\",\"name\":\"eject button\",\"variants\":[]},\"🎦\":{\"unicode\":\"🎦\",\"name\":\"cinema\",\"variants\":[]},\"🔅\":{\"unicode\":\"🔅\",\"name\":\"dim button\",\"variants\":[]},\"🔆\":{\"unicode\":\"🔆\",\"name\":\"bright button\",\"variants\":[]},\"📶\":{\"unicode\":\"📶\",\"name\":\"antenna bars\",\"variants\":[]},\"📳\":{\"unicode\":\"📳\",\"name\":\"vibration mode\",\"variants\":[]},\"📴\":{\"unicode\":\"📴\",\"name\":\"mobile phone off\",\"variants\":[]},\"♀️\":{\"unicode\":\"♀️\",\"name\":\"female sign\",\"variants\":[]},\"♂️\":{\"unicode\":\"♂️\",\"name\":\"male sign\",\"variants\":[]},\"⚧️\":{\"unicode\":\"⚧️\",\"name\":\"transgender symbol\",\"variants\":[]},\"✖️\":{\"unicode\":\"✖️\",\"name\":\"multiply\",\"variants\":[]},\"➕\":{\"unicode\":\"➕\",\"name\":\"plus\",\"variants\":[]},\"➖\":{\"unicode\":\"➖\",\"name\":\"minus\",\"variants\":[]},\"➗\":{\"unicode\":\"➗\",\"name\":\"divide\",\"variants\":[]},\"♾️\":{\"unicode\":\"♾️\",\"name\":\"infinity\",\"variants\":[]},\"‼️\":{\"unicode\":\"‼️\",\"name\":\"double exclamation mark\",\"variants\":[]},\"⁉️\":{\"unicode\":\"⁉️\",\"name\":\"exclamation question mark\",\"variants\":[]},\"❓\":{\"unicode\":\"❓\",\"name\":\"question mark\",\"variants\":[]},\"❔\":{\"unicode\":\"❔\",\"name\":\"white question mark\",\"variants\":[]},\"❕\":{\"unicode\":\"❕\",\"name\":\"white exclamation mark\",\"variants\":[]},\"❗\":{\"unicode\":\"❗\",\"name\":\"exclamation mark\",\"variants\":[]},\"〰️\":{\"unicode\":\"〰️\",\"name\":\"wavy dash\",\"variants\":[]},\"💱\":{\"unicode\":\"💱\",\"name\":\"currency exchange\",\"variants\":[]},\"💲\":{\"unicode\":\"💲\",\"name\":\"heavy dollar sign\",\"variants\":[]},\"⚕️\":{\"unicode\":\"⚕️\",\"name\":\"medical symbol\",\"variants\":[]},\"♻️\":{\"unicode\":\"♻️\",\"name\":\"recycling symbol\",\"variants\":[]},\"⚜️\":{\"unicode\":\"⚜️\",\"name\":\"fleur-de-lis\",\"variants\":[]},\"🔱\":{\"unicode\":\"🔱\",\"name\":\"trident emblem\",\"variants\":[]},\"📛\":{\"unicode\":\"📛\",\"name\":\"name badge\",\"variants\":[]},\"🔰\":{\"unicode\":\"🔰\",\"name\":\"Japanese symbol for beginner\",\"variants\":[]},\"⭕\":{\"unicode\":\"⭕\",\"name\":\"hollow red circle\",\"variants\":[]},\"✅\":{\"unicode\":\"✅\",\"name\":\"check mark button\",\"variants\":[]},\"☑️\":{\"unicode\":\"☑️\",\"name\":\"check box with check\",\"variants\":[]},\"✔️\":{\"unicode\":\"✔️\",\"name\":\"check mark\",\"variants\":[]},\"❌\":{\"unicode\":\"❌\",\"name\":\"cross mark\",\"variants\":[]},\"❎\":{\"unicode\":\"❎\",\"name\":\"cross mark button\",\"variants\":[]},\"➰\":{\"unicode\":\"➰\",\"name\":\"curly loop\",\"variants\":[]},\"➿\":{\"unicode\":\"➿\",\"name\":\"double curly loop\",\"variants\":[]},\"〽️\":{\"unicode\":\"〽️\",\"name\":\"part alternation mark\",\"variants\":[]},\"✳️\":{\"unicode\":\"✳️\",\"name\":\"eight-spoked asterisk\",\"variants\":[]},\"✴️\":{\"unicode\":\"✴️\",\"name\":\"eight-pointed star\",\"variants\":[]},\"❇️\":{\"unicode\":\"❇️\",\"name\":\"sparkle\",\"variants\":[]},\"©️\":{\"unicode\":\"©️\",\"name\":\"copyright\",\"variants\":[]},\"®️\":{\"unicode\":\"®️\",\"name\":\"registered\",\"variants\":[]},\"™️\":{\"unicode\":\"™️\",\"name\":\"trade mark\",\"variants\":[]},\"#️⃣\":{\"unicode\":\"#️⃣\",\"name\":\"\",\"variants\":[]},\"*️⃣\":{\"unicode\":\"*️⃣\",\"name\":\"keycap: *\",\"variants\":[]},\"0️⃣\":{\"unicode\":\"0️⃣\",\"name\":\"keycap: 0\",\"variants\":[]},\"1️⃣\":{\"unicode\":\"1️⃣\",\"name\":\"keycap: 1\",\"variants\":[]},\"2️⃣\":{\"unicode\":\"2️⃣\",\"name\":\"keycap: 2\",\"variants\":[]},\"3️⃣\":{\"unicode\":\"3️⃣\",\"name\":\"keycap: 3\",\"variants\":[]},\"4️⃣\":{\"unicode\":\"4️⃣\",\"name\":\"keycap: 4\",\"variants\":[]},\"5️⃣\":{\"unicode\":\"5️⃣\",\"name\":\"keycap: 5\",\"variants\":[]},\"6️⃣\":{\"unicode\":\"6️⃣\",\"name\":\"keycap: 6\",\"variants\":[]},\"7️⃣\":{\"unicode\":\"7️⃣\",\"name\":\"keycap: 7\",\"variants\":[]},\"8️⃣\":{\"unicode\":\"8️⃣\",\"name\":\"keycap: 8\",\"variants\":[]},\"9️⃣\":{\"unicode\":\"9️⃣\",\"name\":\"keycap: 9\",\"variants\":[]},\"🔟\":{\"unicode\":\"🔟\",\"name\":\"keycap: 10\",\"variants\":[]},\"🔠\":{\"unicode\":\"🔠\",\"name\":\"input latin uppercase\",\"variants\":[]},\"🔡\":{\"unicode\":\"🔡\",\"name\":\"input latin lowercase\",\"variants\":[]},\"🔢\":{\"unicode\":\"🔢\",\"name\":\"input numbers\",\"variants\":[]},\"🔣\":{\"unicode\":\"🔣\",\"name\":\"input symbols\",\"variants\":[]},\"🔤\":{\"unicode\":\"🔤\",\"name\":\"input latin letters\",\"variants\":[]},\"🅰️\":{\"unicode\":\"🅰️\",\"name\":\"A button (blood type)\",\"variants\":[]},\"🆎\":{\"unicode\":\"🆎\",\"name\":\"AB button (blood type)\",\"variants\":[]},\"🅱️\":{\"unicode\":\"🅱️\",\"name\":\"B button (blood type)\",\"variants\":[]},\"🆑\":{\"unicode\":\"🆑\",\"name\":\"CL button\",\"variants\":[]},\"🆒\":{\"unicode\":\"🆒\",\"name\":\"COOL button\",\"variants\":[]},\"🆓\":{\"unicode\":\"🆓\",\"name\":\"FREE button\",\"variants\":[]},\"ℹ️\":{\"unicode\":\"ℹ️\",\"name\":\"information\",\"variants\":[]},\"🆔\":{\"unicode\":\"🆔\",\"name\":\"ID button\",\"variants\":[]},\"Ⓜ️\":{\"unicode\":\"Ⓜ️\",\"name\":\"circled M\",\"variants\":[]},\"🆕\":{\"unicode\":\"🆕\",\"name\":\"NEW button\",\"variants\":[]},\"🆖\":{\"unicode\":\"🆖\",\"name\":\"NG button\",\"variants\":[]},\"🅾️\":{\"unicode\":\"🅾️\",\"name\":\"O button (blood type)\",\"variants\":[]},\"🆗\":{\"unicode\":\"🆗\",\"name\":\"OK button\",\"variants\":[]},\"🅿️\":{\"unicode\":\"🅿️\",\"name\":\"P button\",\"variants\":[]},\"🆘\":{\"unicode\":\"🆘\",\"name\":\"SOS button\",\"variants\":[]},\"🆙\":{\"unicode\":\"🆙\",\"name\":\"UP! button\",\"variants\":[]},\"🆚\":{\"unicode\":\"🆚\",\"name\":\"VS button\",\"variants\":[]},\"🈁\":{\"unicode\":\"🈁\",\"name\":\"Japanese â€œhereâ€� button\",\"variants\":[]},\"🈂️\":{\"unicode\":\"🈂️\",\"name\":\"Japanese â€œservice chargeâ€� button\",\"variants\":[]},\"🈷️\":{\"unicode\":\"🈷️\",\"name\":\"Japanese â€œmonthly amountâ€� button\",\"variants\":[]},\"🈶\":{\"unicode\":\"🈶\",\"name\":\"Japanese â€œnot free of chargeâ€� button\",\"variants\":[]},\"🈯\":{\"unicode\":\"🈯\",\"name\":\"Japanese â€œreservedâ€� button\",\"variants\":[]},\"🉐\":{\"unicode\":\"🉐\",\"name\":\"Japanese â€œbargainâ€� button\",\"variants\":[]},\"🈹\":{\"unicode\":\"🈹\",\"name\":\"Japanese â€œdiscountâ€� button\",\"variants\":[]},\"🈚\":{\"unicode\":\"🈚\",\"name\":\"Japanese â€œfree of chargeâ€� button\",\"variants\":[]},\"🈲\":{\"unicode\":\"🈲\",\"name\":\"Japanese â€œprohibitedâ€� button\",\"variants\":[]},\"🉑\":{\"unicode\":\"🉑\",\"name\":\"Japanese â€œacceptableâ€� button\",\"variants\":[]},\"🈸\":{\"unicode\":\"🈸\",\"name\":\"Japanese â€œapplicationâ€� button\",\"variants\":[]},\"🈴\":{\"unicode\":\"🈴\",\"name\":\"Japanese â€œpassing gradeâ€� button\",\"variants\":[]},\"🈳\":{\"unicode\":\"🈳\",\"name\":\"Japanese â€œvacancyâ€� button\",\"variants\":[]},\"㊗️\":{\"unicode\":\"㊗️\",\"name\":\"Japanese â€œcongratulationsâ€� button\",\"variants\":[]},\"㊙️\":{\"unicode\":\"㊙️\",\"name\":\"Japanese â€œsecretâ€� button\",\"variants\":[]},\"🈺\":{\"unicode\":\"🈺\",\"name\":\"Japanese â€œopen for businessâ€� button\",\"variants\":[]},\"🈵\":{\"unicode\":\"🈵\",\"name\":\"Japanese â€œno vacancyâ€� button\",\"variants\":[]},\"🔴\":{\"unicode\":\"🔴\",\"name\":\"red circle\",\"variants\":[]},\"🟠\":{\"unicode\":\"🟠\",\"name\":\"orange circle\",\"variants\":[]},\"🟡\":{\"unicode\":\"🟡\",\"name\":\"yellow circle\",\"variants\":[]},\"🟢\":{\"unicode\":\"🟢\",\"name\":\"green circle\",\"variants\":[]},\"🔵\":{\"unicode\":\"🔵\",\"name\":\"blue circle\",\"variants\":[]},\"🟣\":{\"unicode\":\"🟣\",\"name\":\"purple circle\",\"variants\":[]},\"🟤\":{\"unicode\":\"🟤\",\"name\":\"brown circle\",\"variants\":[]},\"⚫\":{\"unicode\":\"⚫\",\"name\":\"black circle\",\"variants\":[]},\"⚪\":{\"unicode\":\"⚪\",\"name\":\"white circle\",\"variants\":[]},\"🟥\":{\"unicode\":\"🟥\",\"name\":\"red square\",\"variants\":[]},\"🟧\":{\"unicode\":\"🟧\",\"name\":\"orange square\",\"variants\":[]},\"🟨\":{\"unicode\":\"🟨\",\"name\":\"yellow square\",\"variants\":[]},\"🟩\":{\"unicode\":\"🟩\",\"name\":\"green square\",\"variants\":[]},\"🟦\":{\"unicode\":\"🟦\",\"name\":\"blue square\",\"variants\":[]},\"🟪\":{\"unicode\":\"🟪\",\"name\":\"purple square\",\"variants\":[]},\"🟫\":{\"unicode\":\"🟫\",\"name\":\"brown square\",\"variants\":[]},\"⬛\":{\"unicode\":\"⬛\",\"name\":\"black large square\",\"variants\":[]},\"⬜\":{\"unicode\":\"⬜\",\"name\":\"white large square\",\"variants\":[]},\"◼️\":{\"unicode\":\"◼️\",\"name\":\"black medium square\",\"variants\":[]},\"◻️\":{\"unicode\":\"◻️\",\"name\":\"white medium square\",\"variants\":[]},\"◾\":{\"unicode\":\"◾\",\"name\":\"black medium-small square\",\"variants\":[]},\"◽\":{\"unicode\":\"◽\",\"name\":\"white medium-small square\",\"variants\":[]},\"▪️\":{\"unicode\":\"▪️\",\"name\":\"black small square\",\"variants\":[]},\"▫️\":{\"unicode\":\"▫️\",\"name\":\"white small square\",\"variants\":[]},\"🔶\":{\"unicode\":\"🔶\",\"name\":\"large orange diamond\",\"variants\":[]},\"🔷\":{\"unicode\":\"🔷\",\"name\":\"large blue diamond\",\"variants\":[]},\"🔸\":{\"unicode\":\"🔸\",\"name\":\"small orange diamond\",\"variants\":[]},\"🔹\":{\"unicode\":\"🔹\",\"name\":\"small blue diamond\",\"variants\":[]},\"🔺\":{\"unicode\":\"🔺\",\"name\":\"red triangle pointed up\",\"variants\":[]},\"🔻\":{\"unicode\":\"🔻\",\"name\":\"red triangle pointed down\",\"variants\":[]},\"💠\":{\"unicode\":\"💠\",\"name\":\"diamond with a dot\",\"variants\":[]},\"🔘\":{\"unicode\":\"🔘\",\"name\":\"radio button\",\"variants\":[]},\"🔳\":{\"unicode\":\"🔳\",\"name\":\"white square button\",\"variants\":[]},\"🔲\":{\"unicode\":\"🔲\",\"name\":\"black square button\",\"variants\":[]}}},\"FLAGS\":{\"type\":\"FLAGS\",\"emojis\":{\"🏁\":{\"unicode\":\"🏁\",\"name\":\"chequered flag\",\"variants\":[]},\"🚩\":{\"unicode\":\"🚩\",\"name\":\"triangular flag\",\"variants\":[]},\"🎌\":{\"unicode\":\"🎌\",\"name\":\"crossed flags\",\"variants\":[]},\"🏴\":{\"unicode\":\"🏴\",\"name\":\"black flag\",\"variants\":[]},\"🏳️\":{\"unicode\":\"🏳️\",\"name\":\"white flag\",\"variants\":[]},\"🏳️‍🌈\":{\"unicode\":\"🏳️‍🌈\",\"name\":\"rainbow flag\",\"variants\":[]},\"🏳️‍⚧️\":{\"unicode\":\"🏳️‍⚧️\",\"name\":\"transgender flag\",\"variants\":[]},\"🏴‍☠️\":{\"unicode\":\"🏴‍☠️\",\"name\":\"pirate flag\",\"variants\":[]},\"🇦🇨\":{\"unicode\":\"🇦🇨\",\"name\":\"flag: Ascension Island\",\"variants\":[]},\"🇦🇩\":{\"unicode\":\"🇦🇩\",\"name\":\"flag: Andorra\",\"variants\":[]},\"🇦🇪\":{\"unicode\":\"🇦🇪\",\"name\":\"flag: United Arab Emirates\",\"variants\":[]},\"🇦🇫\":{\"unicode\":\"🇦🇫\",\"name\":\"flag: Afghanistan\",\"variants\":[]},\"🇦🇬\":{\"unicode\":\"🇦🇬\",\"name\":\"flag: Antigua \\u0026 Barbuda\",\"variants\":[]},\"🇦🇮\":{\"unicode\":\"🇦🇮\",\"name\":\"flag: Anguilla\",\"variants\":[]},\"🇦🇱\":{\"unicode\":\"🇦🇱\",\"name\":\"flag: Albania\",\"variants\":[]},\"🇦🇲\":{\"unicode\":\"🇦🇲\",\"name\":\"flag: Armenia\",\"variants\":[]},\"🇦🇴\":{\"unicode\":\"🇦🇴\",\"name\":\"flag: Angola\",\"variants\":[]},\"🇦🇶\":{\"unicode\":\"🇦🇶\",\"name\":\"flag: Antarctica\",\"variants\":[]},\"🇦🇷\":{\"unicode\":\"🇦🇷\",\"name\":\"flag: Argentina\",\"variants\":[]},\"🇦🇸\":{\"unicode\":\"🇦🇸\",\"name\":\"flag: American Samoa\",\"variants\":[]},\"🇦🇹\":{\"unicode\":\"🇦🇹\",\"name\":\"flag: Austria\",\"variants\":[]},\"🇦🇺\":{\"unicode\":\"🇦🇺\",\"name\":\"flag: Australia\",\"variants\":[]},\"🇦🇼\":{\"unicode\":\"🇦🇼\",\"name\":\"flag: Aruba\",\"variants\":[]},\"🇦🇽\":{\"unicode\":\"🇦🇽\",\"name\":\"flag: Ã…land Islands\",\"variants\":[]},\"🇦🇿\":{\"unicode\":\"🇦🇿\",\"name\":\"flag: Azerbaijan\",\"variants\":[]},\"🇧🇦\":{\"unicode\":\"🇧🇦\",\"name\":\"flag: Bosnia \\u0026 Herzegovina\",\"variants\":[]},\"🇧🇧\":{\"unicode\":\"🇧🇧\",\"name\":\"flag: Barbados\",\"variants\":[]},\"🇧🇩\":{\"unicode\":\"🇧🇩\",\"name\":\"flag: Bangladesh\",\"variants\":[]},\"🇧🇪\":{\"unicode\":\"🇧🇪\",\"name\":\"flag: Belgium\",\"variants\":[]},\"🇧🇫\":{\"unicode\":\"🇧🇫\",\"name\":\"flag: Burkina Faso\",\"variants\":[]},\"🇧🇬\":{\"unicode\":\"🇧🇬\",\"name\":\"flag: Bulgaria\",\"variants\":[]},\"🇧🇭\":{\"unicode\":\"🇧🇭\",\"name\":\"flag: Bahrain\",\"variants\":[]},\"🇧🇮\":{\"unicode\":\"🇧🇮\",\"name\":\"flag: Burundi\",\"variants\":[]},\"🇧🇯\":{\"unicode\":\"🇧🇯\",\"name\":\"flag: Benin\",\"variants\":[]},\"🇧🇱\":{\"unicode\":\"🇧🇱\",\"name\":\"flag: St. BarthÃ©lemy\",\"variants\":[]},\"🇧🇲\":{\"unicode\":\"🇧🇲\",\"name\":\"flag: Bermuda\",\"variants\":[]},\"🇧🇳\":{\"unicode\":\"🇧🇳\",\"name\":\"flag: Brunei\",\"variants\":[]},\"🇧🇴\":{\"unicode\":\"🇧🇴\",\"name\":\"flag: Bolivia\",\"variants\":[]},\"🇧🇶\":{\"unicode\":\"🇧🇶\",\"name\":\"flag: Caribbean Netherlands\",\"variants\":[]},\"🇧🇷\":{\"unicode\":\"🇧🇷\",\"name\":\"flag: Brazil\",\"variants\":[]},\"🇧🇸\":{\"unicode\":\"🇧🇸\",\"name\":\"flag: Bahamas\",\"variants\":[]},\"🇧🇹\":{\"unicode\":\"🇧🇹\",\"name\":\"flag: Bhutan\",\"variants\":[]},\"🇧🇻\":{\"unicode\":\"🇧🇻\",\"name\":\"flag: Bouvet Island\",\"variants\":[]},\"🇧🇼\":{\"unicode\":\"🇧🇼\",\"name\":\"flag: Botswana\",\"variants\":[]},\"🇧🇾\":{\"unicode\":\"🇧🇾\",\"name\":\"flag: Belarus\",\"variants\":[]},\"🇧🇿\":{\"unicode\":\"🇧🇿\",\"name\":\"flag: Belize\",\"variants\":[]},\"🇨🇦\":{\"unicode\":\"🇨🇦\",\"name\":\"flag: Canada\",\"variants\":[]},\"🇨🇨\":{\"unicode\":\"🇨🇨\",\"name\":\"flag: Cocos (Keeling) Islands\",\"variants\":[]},\"🇨🇩\":{\"unicode\":\"🇨🇩\",\"name\":\"flag: Congo - Kinshasa\",\"variants\":[]},\"🇨🇫\":{\"unicode\":\"🇨🇫\",\"name\":\"flag: Central African Republic\",\"variants\":[]},\"🇨🇬\":{\"unicode\":\"🇨🇬\",\"name\":\"flag: Congo - Brazzaville\",\"variants\":[]},\"🇨🇭\":{\"unicode\":\"🇨🇭\",\"name\":\"flag: Switzerland\",\"variants\":[]},\"🇨🇮\":{\"unicode\":\"🇨🇮\",\"name\":\"flag: CÃ´te dâ€™Ivoire\",\"variants\":[]},\"🇨🇰\":{\"unicode\":\"🇨🇰\",\"name\":\"flag: Cook Islands\",\"variants\":[]},\"🇨🇱\":{\"unicode\":\"🇨🇱\",\"name\":\"flag: Chile\",\"variants\":[]},\"🇨🇲\":{\"unicode\":\"🇨🇲\",\"name\":\"flag: Cameroon\",\"variants\":[]},\"🇨🇳\":{\"unicode\":\"🇨🇳\",\"name\":\"flag: China\",\"variants\":[]},\"🇨🇴\":{\"unicode\":\"🇨🇴\",\"name\":\"flag: Colombia\",\"variants\":[]},\"🇨🇵\":{\"unicode\":\"🇨🇵\",\"name\":\"flag: Clipperton Island\",\"variants\":[]},\"🇨🇷\":{\"unicode\":\"🇨🇷\",\"name\":\"flag: Costa Rica\",\"variants\":[]},\"🇨🇺\":{\"unicode\":\"🇨🇺\",\"name\":\"flag: Cuba\",\"variants\":[]},\"🇨🇻\":{\"unicode\":\"🇨🇻\",\"name\":\"flag: Cape Verde\",\"variants\":[]},\"🇨🇼\":{\"unicode\":\"🇨🇼\",\"name\":\"flag: CuraÃ§ao\",\"variants\":[]},\"🇨🇽\":{\"unicode\":\"🇨🇽\",\"name\":\"flag: Christmas Island\",\"variants\":[]},\"🇨🇾\":{\"unicode\":\"🇨🇾\",\"name\":\"flag: Cyprus\",\"variants\":[]},\"🇨🇿\":{\"unicode\":\"🇨🇿\",\"name\":\"flag: Czechia\",\"variants\":[]},\"🇩🇪\":{\"unicode\":\"🇩🇪\",\"name\":\"flag: Germany\",\"variants\":[]},\"🇩🇬\":{\"unicode\":\"🇩🇬\",\"name\":\"flag: Diego Garcia\",\"variants\":[]},\"🇩🇯\":{\"unicode\":\"🇩🇯\",\"name\":\"flag: Djibouti\",\"variants\":[]},\"🇩🇰\":{\"unicode\":\"🇩🇰\",\"name\":\"flag: Denmark\",\"variants\":[]},\"🇩🇲\":{\"unicode\":\"🇩🇲\",\"name\":\"flag: Dominica\",\"variants\":[]},\"🇩🇴\":{\"unicode\":\"🇩🇴\",\"name\":\"flag: Dominican Republic\",\"variants\":[]},\"🇩🇿\":{\"unicode\":\"🇩🇿\",\"name\":\"flag: Algeria\",\"variants\":[]},\"🇪🇦\":{\"unicode\":\"🇪🇦\",\"name\":\"flag: Ceuta \\u0026 Melilla\",\"variants\":[]},\"🇪🇨\":{\"unicode\":\"🇪🇨\",\"name\":\"flag: Ecuador\",\"variants\":[]},\"🇪🇪\":{\"unicode\":\"🇪🇪\",\"name\":\"flag: Estonia\",\"variants\":[]},\"🇪🇬\":{\"unicode\":\"🇪🇬\",\"name\":\"flag: Egypt\",\"variants\":[]},\"🇪🇭\":{\"unicode\":\"🇪🇭\",\"name\":\"flag: Western Sahara\",\"variants\":[]},\"🇪🇷\":{\"unicode\":\"🇪🇷\",\"name\":\"flag: Eritrea\",\"variants\":[]},\"🇪🇸\":{\"unicode\":\"🇪🇸\",\"name\":\"flag: Spain\",\"variants\":[]},\"🇪🇹\":{\"unicode\":\"🇪🇹\",\"name\":\"flag: Ethiopia\",\"variants\":[]},\"🇪🇺\":{\"unicode\":\"🇪🇺\",\"name\":\"flag: European Union\",\"variants\":[]},\"🇫🇮\":{\"unicode\":\"🇫🇮\",\"name\":\"flag: Finland\",\"variants\":[]},\"🇫🇯\":{\"unicode\":\"🇫🇯\",\"name\":\"flag: Fiji\",\"variants\":[]},\"🇫🇰\":{\"unicode\":\"🇫🇰\",\"name\":\"flag: Falkland Islands\",\"variants\":[]},\"🇫🇲\":{\"unicode\":\"🇫🇲\",\"name\":\"flag: Micronesia\",\"variants\":[]},\"🇫🇴\":{\"unicode\":\"🇫🇴\",\"name\":\"flag: Faroe Islands\",\"variants\":[]},\"🇫🇷\":{\"unicode\":\"🇫🇷\",\"name\":\"flag: France\",\"variants\":[]},\"🇬🇦\":{\"unicode\":\"🇬🇦\",\"name\":\"flag: Gabon\",\"variants\":[]},\"🇬🇧\":{\"unicode\":\"🇬🇧\",\"name\":\"flag: United Kingdom\",\"variants\":[]},\"🇬🇩\":{\"unicode\":\"🇬🇩\",\"name\":\"flag: Grenada\",\"variants\":[]},\"🇬🇪\":{\"unicode\":\"🇬🇪\",\"name\":\"flag: Georgia\",\"variants\":[]},\"🇬🇫\":{\"unicode\":\"🇬🇫\",\"name\":\"flag: French Guiana\",\"variants\":[]},\"🇬🇬\":{\"unicode\":\"🇬🇬\",\"name\":\"flag: Guernsey\",\"variants\":[]},\"🇬🇭\":{\"unicode\":\"🇬🇭\",\"name\":\"flag: Ghana\",\"variants\":[]},\"🇬🇮\":{\"unicode\":\"🇬🇮\",\"name\":\"flag: Gibraltar\",\"variants\":[]},\"🇬🇱\":{\"unicode\":\"🇬🇱\",\"name\":\"flag: Greenland\",\"variants\":[]},\"🇬🇲\":{\"unicode\":\"🇬🇲\",\"name\":\"flag: Gambia\",\"variants\":[]},\"🇬🇳\":{\"unicode\":\"🇬🇳\",\"name\":\"flag: Guinea\",\"variants\":[]},\"🇬🇵\":{\"unicode\":\"🇬🇵\",\"name\":\"flag: Guadeloupe\",\"variants\":[]},\"🇬🇶\":{\"unicode\":\"🇬🇶\",\"name\":\"flag: Equatorial Guinea\",\"variants\":[]},\"🇬🇷\":{\"unicode\":\"🇬🇷\",\"name\":\"flag: Greece\",\"variants\":[]},\"🇬🇸\":{\"unicode\":\"🇬🇸\",\"name\":\"flag: South Georgia \\u0026 South Sandwich Islands\",\"variants\":[]},\"🇬🇹\":{\"unicode\":\"🇬🇹\",\"name\":\"flag: Guatemala\",\"variants\":[]},\"🇬🇺\":{\"unicode\":\"🇬🇺\",\"name\":\"flag: Guam\",\"variants\":[]},\"🇬🇼\":{\"unicode\":\"🇬🇼\",\"name\":\"flag: Guinea-Bissau\",\"variants\":[]},\"🇬🇾\":{\"unicode\":\"🇬🇾\",\"name\":\"flag: Guyana\",\"variants\":[]},\"🇭🇰\":{\"unicode\":\"🇭🇰\",\"name\":\"flag: Hong Kong SAR China\",\"variants\":[]},\"🇭🇲\":{\"unicode\":\"🇭🇲\",\"name\":\"flag: Heard \\u0026 McDonald Islands\",\"variants\":[]},\"🇭🇳\":{\"unicode\":\"🇭🇳\",\"name\":\"flag: Honduras\",\"variants\":[]},\"🇭🇷\":{\"unicode\":\"🇭🇷\",\"name\":\"flag: Croatia\",\"variants\":[]},\"🇭🇹\":{\"unicode\":\"🇭🇹\",\"name\":\"flag: Haiti\",\"variants\":[]},\"🇭🇺\":{\"unicode\":\"🇭🇺\",\"name\":\"flag: Hungary\",\"variants\":[]},\"🇮🇨\":{\"unicode\":\"🇮🇨\",\"name\":\"flag: Canary Islands\",\"variants\":[]},\"🇮🇩\":{\"unicode\":\"🇮🇩\",\"name\":\"flag: Indonesia\",\"variants\":[]},\"🇮🇪\":{\"unicode\":\"🇮🇪\",\"name\":\"flag: Ireland\",\"variants\":[]},\"🇮🇱\":{\"unicode\":\"🇮🇱\",\"name\":\"flag: Israel\",\"variants\":[]},\"🇮🇲\":{\"unicode\":\"🇮🇲\",\"name\":\"flag: Isle of Man\",\"variants\":[]},\"🇮🇳\":{\"unicode\":\"🇮🇳\",\"name\":\"flag: India\",\"variants\":[]},\"🇮🇴\":{\"unicode\":\"🇮🇴\",\"name\":\"flag: British Indian Ocean Territory\",\"variants\":[]},\"🇮🇶\":{\"unicode\":\"🇮🇶\",\"name\":\"flag: Iraq\",\"variants\":[]},\"🇮🇷\":{\"unicode\":\"🇮🇷\",\"name\":\"flag: Iran\",\"variants\":[]},\"🇮🇸\":{\"unicode\":\"🇮🇸\",\"name\":\"flag: Iceland\",\"variants\":[]},\"🇮🇹\":{\"unicode\":\"🇮🇹\",\"name\":\"flag: Italy\",\"variants\":[]},\"🇯🇪\":{\"unicode\":\"🇯🇪\",\"name\":\"flag: Jersey\",\"variants\":[]},\"🇯🇲\":{\"unicode\":\"🇯🇲\",\"name\":\"flag: Jamaica\",\"variants\":[]},\"🇯🇴\":{\"unicode\":\"🇯🇴\",\"name\":\"flag: Jordan\",\"variants\":[]},\"🇯🇵\":{\"unicode\":\"🇯🇵\",\"name\":\"flag: Japan\",\"variants\":[]},\"🇰🇪\":{\"unicode\":\"🇰🇪\",\"name\":\"flag: Kenya\",\"variants\":[]},\"🇰🇬\":{\"unicode\":\"🇰🇬\",\"name\":\"flag: Kyrgyzstan\",\"variants\":[]},\"🇰🇭\":{\"unicode\":\"🇰🇭\",\"name\":\"flag: Cambodia\",\"variants\":[]},\"🇰🇮\":{\"unicode\":\"🇰🇮\",\"name\":\"flag: Kiribati\",\"variants\":[]},\"🇰🇲\":{\"unicode\":\"🇰🇲\",\"name\":\"flag: Comoros\",\"variants\":[]},\"🇰🇳\":{\"unicode\":\"🇰🇳\",\"name\":\"flag: St. Kitts \\u0026 Nevis\",\"variants\":[]},\"🇰🇵\":{\"unicode\":\"🇰🇵\",\"name\":\"flag: North Korea\",\"variants\":[]},\"🇰🇷\":{\"unicode\":\"🇰🇷\",\"name\":\"flag: South Korea\",\"variants\":[]},\"🇰🇼\":{\"unicode\":\"🇰🇼\",\"name\":\"flag: Kuwait\",\"variants\":[]},\"🇰🇾\":{\"unicode\":\"🇰🇾\",\"name\":\"flag: Cayman Islands\",\"variants\":[]},\"🇰🇿\":{\"unicode\":\"🇰🇿\",\"name\":\"flag: Kazakhstan\",\"variants\":[]},\"🇱🇦\":{\"unicode\":\"🇱🇦\",\"name\":\"flag: Laos\",\"variants\":[]},\"🇱🇧\":{\"unicode\":\"🇱🇧\",\"name\":\"flag: Lebanon\",\"variants\":[]},\"🇱🇨\":{\"unicode\":\"🇱🇨\",\"name\":\"flag: St. Lucia\",\"variants\":[]},\"🇱🇮\":{\"unicode\":\"🇱🇮\",\"name\":\"flag: Liechtenstein\",\"variants\":[]},\"🇱🇰\":{\"unicode\":\"🇱🇰\",\"name\":\"flag: Sri Lanka\",\"variants\":[]},\"🇱🇷\":{\"unicode\":\"🇱🇷\",\"name\":\"flag: Liberia\",\"variants\":[]},\"🇱🇸\":{\"unicode\":\"🇱🇸\",\"name\":\"flag: Lesotho\",\"variants\":[]},\"🇱🇹\":{\"unicode\":\"🇱🇹\",\"name\":\"flag: Lithuania\",\"variants\":[]},\"🇱🇺\":{\"unicode\":\"🇱🇺\",\"name\":\"flag: Luxembourg\",\"variants\":[]},\"🇱🇻\":{\"unicode\":\"🇱🇻\",\"name\":\"flag: Latvia\",\"variants\":[]},\"🇱🇾\":{\"unicode\":\"🇱🇾\",\"name\":\"flag: Libya\",\"variants\":[]},\"🇲🇦\":{\"unicode\":\"🇲🇦\",\"name\":\"flag: Morocco\",\"variants\":[]},\"🇲🇨\":{\"unicode\":\"🇲🇨\",\"name\":\"flag: Monaco\",\"variants\":[]},\"🇲🇩\":{\"unicode\":\"🇲🇩\",\"name\":\"flag: Moldova\",\"variants\":[]},\"🇲🇪\":{\"unicode\":\"🇲🇪\",\"name\":\"flag: Montenegro\",\"variants\":[]},\"🇲🇫\":{\"unicode\":\"🇲🇫\",\"name\":\"flag: St. Martin\",\"variants\":[]},\"🇲🇬\":{\"unicode\":\"🇲🇬\",\"name\":\"flag: Madagascar\",\"variants\":[]},\"🇲🇭\":{\"unicode\":\"🇲🇭\",\"name\":\"flag: Marshall Islands\",\"variants\":[]},\"🇲🇰\":{\"unicode\":\"🇲🇰\",\"name\":\"flag: North Macedonia\",\"variants\":[]},\"🇲🇱\":{\"unicode\":\"🇲🇱\",\"name\":\"flag: Mali\",\"variants\":[]},\"🇲🇲\":{\"unicode\":\"🇲🇲\",\"name\":\"flag: Myanmar (Burma)\",\"variants\":[]},\"🇲🇳\":{\"unicode\":\"🇲🇳\",\"name\":\"flag: Mongolia\",\"variants\":[]},\"🇲🇴\":{\"unicode\":\"🇲🇴\",\"name\":\"flag: Macao SAR China\",\"variants\":[]},\"🇲🇵\":{\"unicode\":\"🇲🇵\",\"name\":\"flag: Northern Mariana Islands\",\"variants\":[]},\"🇲🇶\":{\"unicode\":\"🇲🇶\",\"name\":\"flag: Martinique\",\"variants\":[]},\"🇲🇷\":{\"unicode\":\"🇲🇷\",\"name\":\"flag: Mauritania\",\"variants\":[]},\"🇲🇸\":{\"unicode\":\"🇲🇸\",\"name\":\"flag: Montserrat\",\"variants\":[]},\"🇲🇹\":{\"unicode\":\"🇲🇹\",\"name\":\"flag: Malta\",\"variants\":[]},\"🇲🇺\":{\"unicode\":\"🇲🇺\",\"name\":\"flag: Mauritius\",\"variants\":[]},\"🇲🇻\":{\"unicode\":\"🇲🇻\",\"name\":\"flag: Maldives\",\"variants\":[]},\"🇲🇼\":{\"unicode\":\"🇲🇼\",\"name\":\"flag: Malawi\",\"variants\":[]},\"🇲🇽\":{\"unicode\":\"🇲🇽\",\"name\":\"flag: Mexico\",\"variants\":[]},\"🇲🇾\":{\"unicode\":\"🇲🇾\",\"name\":\"flag: Malaysia\",\"variants\":[]},\"🇲🇿\":{\"unicode\":\"🇲🇿\",\"name\":\"flag: Mozambique\",\"variants\":[]},\"🇳🇦\":{\"unicode\":\"🇳🇦\",\"name\":\"flag: Namibia\",\"variants\":[]},\"🇳🇨\":{\"unicode\":\"🇳🇨\",\"name\":\"flag: New Caledonia\",\"variants\":[]},\"🇳🇪\":{\"unicode\":\"🇳🇪\",\"name\":\"flag: Niger\",\"variants\":[]},\"🇳🇫\":{\"unicode\":\"🇳🇫\",\"name\":\"flag: Norfolk Island\",\"variants\":[]},\"🇳🇬\":{\"unicode\":\"🇳🇬\",\"name\":\"flag: Nigeria\",\"variants\":[]},\"🇳🇮\":{\"unicode\":\"🇳🇮\",\"name\":\"flag: Nicaragua\",\"variants\":[]},\"🇳🇱\":{\"unicode\":\"🇳🇱\",\"name\":\"flag: Netherlands\",\"variants\":[]},\"🇳🇴\":{\"unicode\":\"🇳🇴\",\"name\":\"flag: Norway\",\"variants\":[]},\"🇳🇵\":{\"unicode\":\"🇳🇵\",\"name\":\"flag: Nepal\",\"variants\":[]},\"🇳🇷\":{\"unicode\":\"🇳🇷\",\"name\":\"flag: Nauru\",\"variants\":[]},\"🇳🇺\":{\"unicode\":\"🇳🇺\",\"name\":\"flag: Niue\",\"variants\":[]},\"🇳🇿\":{\"unicode\":\"🇳🇿\",\"name\":\"flag: New Zealand\",\"variants\":[]},\"🇴🇲\":{\"unicode\":\"🇴🇲\",\"name\":\"flag: Oman\",\"variants\":[]},\"🇵🇦\":{\"unicode\":\"🇵🇦\",\"name\":\"flag: Panama\",\"variants\":[]},\"🇵🇪\":{\"unicode\":\"🇵🇪\",\"name\":\"flag: Peru\",\"variants\":[]},\"🇵🇫\":{\"unicode\":\"🇵🇫\",\"name\":\"flag: French Polynesia\",\"variants\":[]},\"🇵🇬\":{\"unicode\":\"🇵🇬\",\"name\":\"flag: Papua New Guinea\",\"variants\":[]},\"🇵🇭\":{\"unicode\":\"🇵🇭\",\"name\":\"flag: Philippines\",\"variants\":[]},\"🇵🇰\":{\"unicode\":\"🇵🇰\",\"name\":\"flag: Pakistan\",\"variants\":[]},\"🇵🇱\":{\"unicode\":\"🇵🇱\",\"name\":\"flag: Poland\",\"variants\":[]},\"🇵🇲\":{\"unicode\":\"🇵🇲\",\"name\":\"flag: St. Pierre \\u0026 Miquelon\",\"variants\":[]},\"🇵🇳\":{\"unicode\":\"🇵🇳\",\"name\":\"flag: Pitcairn Islands\",\"variants\":[]},\"🇵🇷\":{\"unicode\":\"🇵🇷\",\"name\":\"flag: Puerto Rico\",\"variants\":[]},\"🇵🇸\":{\"unicode\":\"🇵🇸\",\"name\":\"flag: Palestinian Territories\",\"variants\":[]},\"🇵🇹\":{\"unicode\":\"🇵🇹\",\"name\":\"flag: Portugal\",\"variants\":[]},\"🇵🇼\":{\"unicode\":\"🇵🇼\",\"name\":\"flag: Palau\",\"variants\":[]},\"🇵🇾\":{\"unicode\":\"🇵🇾\",\"name\":\"flag: Paraguay\",\"variants\":[]},\"🇶🇦\":{\"unicode\":\"🇶🇦\",\"name\":\"flag: Qatar\",\"variants\":[]},\"🇷🇪\":{\"unicode\":\"🇷🇪\",\"name\":\"flag: RÃ©union\",\"variants\":[]},\"🇷🇴\":{\"unicode\":\"🇷🇴\",\"name\":\"flag: Romania\",\"variants\":[]},\"🇷🇸\":{\"unicode\":\"🇷🇸\",\"name\":\"flag: Serbia\",\"variants\":[]},\"🇷🇺\":{\"unicode\":\"🇷🇺\",\"name\":\"flag: Russia\",\"variants\":[]},\"🇷🇼\":{\"unicode\":\"🇷🇼\",\"name\":\"flag: Rwanda\",\"variants\":[]},\"🇸🇦\":{\"unicode\":\"🇸🇦\",\"name\":\"flag: Saudi Arabia\",\"variants\":[]},\"🇸🇧\":{\"unicode\":\"🇸🇧\",\"name\":\"flag: Solomon Islands\",\"variants\":[]},\"🇸🇨\":{\"unicode\":\"🇸🇨\",\"name\":\"flag: Seychelles\",\"variants\":[]},\"🇸🇩\":{\"unicode\":\"🇸🇩\",\"name\":\"flag: Sudan\",\"variants\":[]},\"🇸🇪\":{\"unicode\":\"🇸🇪\",\"name\":\"flag: Sweden\",\"variants\":[]},\"🇸🇬\":{\"unicode\":\"🇸🇬\",\"name\":\"flag: Singapore\",\"variants\":[]},\"🇸🇭\":{\"unicode\":\"🇸🇭\",\"name\":\"flag: St. Helena\",\"variants\":[]},\"🇸🇮\":{\"unicode\":\"🇸🇮\",\"name\":\"flag: Slovenia\",\"variants\":[]},\"🇸🇯\":{\"unicode\":\"🇸🇯\",\"name\":\"flag: Svalbard \\u0026 Jan Mayen\",\"variants\":[]},\"🇸🇰\":{\"unicode\":\"🇸🇰\",\"name\":\"flag: Slovakia\",\"variants\":[]},\"🇸🇱\":{\"unicode\":\"🇸🇱\",\"name\":\"flag: Sierra Leone\",\"variants\":[]},\"🇸🇲\":{\"unicode\":\"🇸🇲\",\"name\":\"flag: San Marino\",\"variants\":[]},\"🇸🇳\":{\"unicode\":\"🇸🇳\",\"name\":\"flag: Senegal\",\"variants\":[]},\"🇸🇴\":{\"unicode\":\"🇸🇴\",\"name\":\"flag: Somalia\",\"variants\":[]},\"🇸🇷\":{\"unicode\":\"🇸🇷\",\"name\":\"flag: Suriname\",\"variants\":[]},\"🇸🇸\":{\"unicode\":\"🇸🇸\",\"name\":\"flag: South Sudan\",\"variants\":[]},\"🇸🇹\":{\"unicode\":\"🇸🇹\",\"name\":\"flag: SÃ£o TomÃ© \\u0026 PrÃ­ncipe\",\"variants\":[]},\"🇸🇻\":{\"unicode\":\"🇸🇻\",\"name\":\"flag: El Salvador\",\"variants\":[]},\"🇸🇽\":{\"unicode\":\"🇸🇽\",\"name\":\"flag: Sint Maarten\",\"variants\":[]},\"🇸🇾\":{\"unicode\":\"🇸🇾\",\"name\":\"flag: Syria\",\"variants\":[]},\"🇸🇿\":{\"unicode\":\"🇸🇿\",\"name\":\"flag: Eswatini\",\"variants\":[]},\"🇹🇦\":{\"unicode\":\"🇹🇦\",\"name\":\"flag: Tristan da Cunha\",\"variants\":[]},\"🇹🇨\":{\"unicode\":\"🇹🇨\",\"name\":\"flag: Turks \\u0026 Caicos Islands\",\"variants\":[]},\"🇹🇩\":{\"unicode\":\"🇹🇩\",\"name\":\"flag: Chad\",\"variants\":[]},\"🇹🇫\":{\"unicode\":\"🇹🇫\",\"name\":\"flag: French Southern Territories\",\"variants\":[]},\"🇹🇬\":{\"unicode\":\"🇹🇬\",\"name\":\"flag: Togo\",\"variants\":[]},\"🇹🇭\":{\"unicode\":\"🇹🇭\",\"name\":\"flag: Thailand\",\"variants\":[]},\"🇹🇯\":{\"unicode\":\"🇹🇯\",\"name\":\"flag: Tajikistan\",\"variants\":[]},\"🇹🇰\":{\"unicode\":\"🇹🇰\",\"name\":\"flag: Tokelau\",\"variants\":[]},\"🇹🇱\":{\"unicode\":\"🇹🇱\",\"name\":\"flag: Timor-Leste\",\"variants\":[]},\"🇹🇲\":{\"unicode\":\"🇹🇲\",\"name\":\"flag: Turkmenistan\",\"variants\":[]},\"🇹🇳\":{\"unicode\":\"🇹🇳\",\"name\":\"flag: Tunisia\",\"variants\":[]},\"🇹🇴\":{\"unicode\":\"🇹🇴\",\"name\":\"flag: Tonga\",\"variants\":[]},\"🇹🇷\":{\"unicode\":\"🇹🇷\",\"name\":\"flag: Turkey\",\"variants\":[]},\"🇹🇹\":{\"unicode\":\"🇹🇹\",\"name\":\"flag: Trinidad \\u0026 Tobago\",\"variants\":[]},\"🇹🇻\":{\"unicode\":\"🇹🇻\",\"name\":\"flag: Tuvalu\",\"variants\":[]},\"🇹🇼\":{\"unicode\":\"🇹🇼\",\"name\":\"flag: Taiwan\",\"variants\":[]},\"🇹🇿\":{\"unicode\":\"🇹🇿\",\"name\":\"flag: Tanzania\",\"variants\":[]},\"🇺🇦\":{\"unicode\":\"🇺🇦\",\"name\":\"flag: Ukraine\",\"variants\":[]},\"🇺🇬\":{\"unicode\":\"🇺🇬\",\"name\":\"flag: Uganda\",\"variants\":[]},\"🇺🇲\":{\"unicode\":\"🇺🇲\",\"name\":\"flag: U.S. Outlying Islands\",\"variants\":[]},\"🇺🇳\":{\"unicode\":\"🇺🇳\",\"name\":\"flag: United Nations\",\"variants\":[]},\"🇺🇸\":{\"unicode\":\"🇺🇸\",\"name\":\"flag: United States\",\"variants\":[]},\"🇺🇾\":{\"unicode\":\"🇺🇾\",\"name\":\"flag: Uruguay\",\"variants\":[]},\"🇺🇿\":{\"unicode\":\"🇺🇿\",\"name\":\"flag: Uzbekistan\",\"variants\":[]},\"🇻🇦\":{\"unicode\":\"🇻🇦\",\"name\":\"flag: Vatican City\",\"variants\":[]},\"🇻🇨\":{\"unicode\":\"🇻🇨\",\"name\":\"flag: St. Vincent \\u0026 Grenadines\",\"variants\":[]},\"🇻🇪\":{\"unicode\":\"🇻🇪\",\"name\":\"flag: Venezuela\",\"variants\":[]},\"🇻🇬\":{\"unicode\":\"🇻🇬\",\"name\":\"flag: British Virgin Islands\",\"variants\":[]},\"🇻🇮\":{\"unicode\":\"🇻🇮\",\"name\":\"flag: U.S. Virgin Islands\",\"variants\":[]},\"🇻🇳\":{\"unicode\":\"🇻🇳\",\"name\":\"flag: Vietnam\",\"variants\":[]},\"🇻🇺\":{\"unicode\":\"🇻🇺\",\"name\":\"flag: Vanuatu\",\"variants\":[]},\"🇼🇫\":{\"unicode\":\"🇼🇫\",\"name\":\"flag: Wallis \\u0026 Futuna\",\"variants\":[]},\"🇼🇸\":{\"unicode\":\"🇼🇸\",\"name\":\"flag: Samoa\",\"variants\":[]},\"🇽🇰\":{\"unicode\":\"🇽🇰\",\"name\":\"flag: Kosovo\",\"variants\":[]},\"🇾🇪\":{\"unicode\":\"🇾🇪\",\"name\":\"flag: Yemen\",\"variants\":[]},\"🇾🇹\":{\"unicode\":\"🇾🇹\",\"name\":\"flag: Mayotte\",\"variants\":[]},\"🇿🇦\":{\"unicode\":\"🇿🇦\",\"name\":\"flag: South Africa\",\"variants\":[]},\"🇿🇲\":{\"unicode\":\"🇿🇲\",\"name\":\"flag: Zambia\",\"variants\":[]},\"🇿🇼\":{\"unicode\":\"🇿🇼\",\"name\":\"flag: Zimbabwe\",\"variants\":[]},\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\":{\"unicode\":\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\",\"name\":\"flag: England\",\"variants\":[]},\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\":{\"unicode\":\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\",\"name\":\"flag: Scotland\",\"variants\":[]},\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\":{\"unicode\":\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\",\"name\":\"flag: Wales\",\"variants\":[]}}}}"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>System Default</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Auto / Follow System</item>\n        <item>Auto / Follow Battery</item>\n        <item>Dark</item>\n        <item>Light</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram default (Unread then read)</item>\n        <item>From newest to oldest</item>\n        <item>From oldest to newest</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>None</item>\n        <item>\\@</item>\n        <item>at</item>\n        <item>on</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"date_presets\" translatable=\"false\">\n        <item>dd-MM-yyyy</item>\n        <item>dd/MM/yyyy</item>\n        <item>dd.MM.yyyy</item>\n        <item>dd-MM-yy</item>\n        <item>dd/MM/yy</item>\n        <item>dd.MM.yy</item>\n        <item>dd-MMM-yyyy</item>\n        <item>dd/MMM/yyyy</item>\n        <item>dd.MMM.yyyy</item>\n        <item>dd-MMM-yy</item>\n        <item>dd/MMM/yy</item>\n        <item>dd.MMM.yy</item>\n        <item>MM-dd-yyyy</item>\n        <item>MM/dd/yyyy</item>\n        <item>MM.dd.yyyy</item>\n        <item>yyyy-MM-dd</item>\n        <item>yyyy/MM/dd</item>\n        <item>yyyy.MM.dd</item>\n        <item>MM-dd-yy</item>\n        <item>MM/dd/yy</item>\n        <item>MM.dd.yy</item>\n        <item>yy-MM-dd</item>\n        <item>yy/MM/dd</item>\n        <item>yy.MM.dd</item>\n        <item>MMM-dd-yyyy</item>\n        <item>MMM/dd/yyyy</item>\n        <item>MMM.dd.yyyy</item>\n        <item>yyyy-MMM-dd</item>\n        <item>yyyy/MMM/dd</item>\n        <item>yyyy.MMM.dd</item>\n        <item>MMM-dd-yy</item>\n        <item>MMM/dd/yy</item>\n        <item>MMM.dd.yy</item>\n        <item>yy-MMM-dd</item>\n        <item>yy/MMM/dd</item>\n        <item>yy.MMM.dd</item>\n    </string-array>\n    <string-array name=\"time_presets\" translatable=\"false\">\n        <item>hh:mm:ss a</item>\n        <item>h:mm:ss a</item>\n        <item>HH:mm:ss</item>\n        <item>H:mm:ss</item>\n    </string-array>\n\n    <array name=\"logged_in_nav_root_ids\">\n        <item>@id/direct_messages_nav_graph</item>\n        <item>@id/feed_nav_graph</item>\n        <item>@id/profile_nav_graph</item>\n        <item>@id/discover_nav_graph</item>\n        <item>@id/more_nav_graph</item>\n        <item>@id/favorites_nav_graph</item>\n        <item>@id/notification_viewer_nav_graph</item>\n    </array>\n\n    <array name=\"anon_nav_root_ids\">\n        <item>@id/favorites_nav_graph</item>\n        <item>@id/profile_nav_graph</item>\n        <item>@id/more_nav_graph</item>\n    </array>\n\n    <string-array name=\"light_themes\" translatable=\"false\">\n        <item>@string/light_white_theme</item>\n        <item>@string/light_barinsta_theme</item>\n        <item>@string/light_bibliogram_theme</item>\n    </string-array>\n    <string-array name=\"light_theme_values\" translatable=\"false\">\n        <item>@style/AppTheme.Light.White</item>\n        <item>@style/AppTheme.Light.Barinsta</item>\n        <item>@style/AppTheme.Light.Bibliogram</item>\n    </string-array>\n    <string-array name=\"dark_themes\" translatable=\"false\">\n        <item>@string/dark_black_theme</item>\n        <item>@string/dark_material_dark_theme</item>\n    </string-array>\n    <string-array name=\"dark_theme_values\" translatable=\"false\">\n        <item>@style/AppTheme.Dark.Black</item>\n        <item>@style/AppTheme.Dark.MaterialDark</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>secs</item>\n        <item>mins</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_unit_labels\" translatable=\"false\">\n        <item>@string/secs</item>\n        <item>@string/mins</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"ProfilePicView\">\n        <attr name=\"size\" format=\"enum\">\n            <enum name=\"tiny\" value=\"0\" />\n            <enum name=\"small\" value=\"1\" />\n            <enum name=\"regular\" value=\"2\" />\n            <enum name=\"large\" value=\"3\" />\n            <enum name=\"smaller\" value=\"4\" />\n        </attr>\n    </declare-styleable>\n    <declare-styleable name=\"ChatMessageLayout\">\n        <attr name=\"viewPartMain\" format=\"reference\" />\n        <attr name=\"viewPartInfo\" format=\"reference\" />\n    </declare-styleable>\n\n    <attr name=\"dmIncomingBgColor\" format=\"reference\" />\n    <attr name=\"dmOutgoingBgColor\" format=\"reference\" />\n    <attr name=\"dmDateHeaderBgColor\" format=\"reference\" />\n    <attr name=\"dmWaveformBgColor\" format=\"reference\" />\n    <attr name=\"dmWaveformProgressColor\" format=\"reference\" />\n    <attr name=\"dmInputTextColor\" format=\"reference\" />\n\n    <attr name=\"toolbarColor\" format=\"reference\" />\n    <attr name=\"searchInputStyle\" format=\"reference\" />\n    <attr name=\"parentCommentHighlightColor\" format=\"reference\" />\n\n    <declare-styleable name=\"RecordView\">\n        <attr name=\"slide_to_cancel_text\" format=\"string\" />\n        <attr name=\"slide_to_cancel_text_color\" format=\"reference\" />\n        <attr name=\"slide_to_cancel_arrow\" format=\"reference\" />\n        <attr name=\"slide_to_cancel_margin_right\" format=\"dimension\" />\n        <attr name=\"slide_to_cancel_bounds\" format=\"dimension\" />\n        <attr name=\"slide_to_cancel_arrow_color\" format=\"reference\" />\n        <attr name=\"counter_time_color\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"WaveformSeekBar\">\n        <attr name=\"waveformBackgroundColor\" format=\"reference\" />\n        <attr name=\"waveformProgressColor\" format=\"reference\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"TextViewDrawableSize\">\n        <attr name=\"compoundDrawableWidth\" format=\"dimension\" />\n        <attr name=\"compoundDrawableHeight\" format=\"dimension\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/bool.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <bool name=\"isNight\">false</bool>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n\n    <color name=\"text_color_light\">@android:color/primary_text_light</color>\n    <color name=\"text_color_dark\">@android:color/primary_text_dark</color>\n\n    <color name=\"btn_green_background\">#5CE362</color>\n    <color name=\"btn_green_text_color\">@color/text_color_light</color>\n\n    <color name=\"btn_blue_background\">#0288D1</color>\n    <color name=\"btn_blue_text_color\">@color/text_color_light</color>\n\n    <color name=\"btn_red_background\">#E62323</color>\n    <color name=\"btn_red_text_color\">@color/text_color_dark</color>\n\n    <color name=\"btn_purple_background\">#909090</color>\n    <color name=\"btn_lightpink_background\">#FFB6C1</color>\n    <color name=\"btn_lightpink_text_color\">@color/text_color_light</color>\n\n    <color name=\"btn_orange_background\">#FF5500</color>\n    <color name=\"btn_orange_text_color\">@color/white</color>\n    <color name=\"btn_lightorange_background\">#FFBB00</color>\n    <color name=\"btn_lightorange_text_color\">@color/black</color>\n\n    <color name=\"dm_profile_button_color\">#efefef</color>\n\n    <color name=\"parent_comment_dark_materialdark\">#FF082654</color>\n    <color name=\"parent_comment_light_white\">@color/grey_300</color>\n    <!--<color name=\"comment_liked\">#40FF69B4</color>-->\n\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"white_a50\">#80FFFFFF</color>\n\n    <color name=\"black\">#000000</color>\n    <color name=\"black_800\">#121212</color>\n    <color name=\"black_a50\">#80000000</color>\n    <color name=\"black_a80\">#CC000000</color>\n\n    <color name=\"grey_50\">#FAFAFA</color>\n    <color name=\"grey_100\">#F5F5F5</color>\n    <color name=\"grey_200\">#EEEEEE</color>\n    <color name=\"grey_300\">#E0E0E0</color>\n    <color name=\"grey_400\">#BDBDBD</color>\n    <color name=\"grey_500\">#9E9E9E</color>\n    <color name=\"grey_600\">#757575</color>\n    <color name=\"grey_700\">#616161</color>\n    <color name=\"grey_800\">#424242</color>\n    <color name=\"grey_900\">#212121</color>\n    <color name=\"grey_600_a20\">#32757575</color>\n\n    <color name=\"blue_50\">#E3F2FD</color>\n    <color name=\"blue_100\">#BBDEFB</color>\n    <color name=\"blue_200\">#90CAF9</color>\n    <color name=\"blue_300\">#64B5F6</color>\n    <color name=\"blue_400\">#42A5F5</color>\n    <color name=\"blue_500\">#2196F3</color>\n    <color name=\"blue_600\">#1E88E5</color>\n    <color name=\"blue_700\">#1976D2</color>\n    <color name=\"blue_800\">#1565C0</color>\n    <color name=\"blue_900\">#0D47A1</color>\n    <color name=\"blue_A100\">#82B1FF</color>\n    <color name=\"blue_A200\">#448AFF</color>\n    <color name=\"blue_A400\">#2979FF</color>\n    <color name=\"blue_A700\">#2962FF</color>\n\n    <color name=\"light_blue_900\">#01579b</color>\n\n    <color name=\"indigo_900\">#1A237E</color>\n\n    <color name=\"brown_50\">#EFEBE9</color>\n    <color name=\"brown_100\">#D7CCC8</color>\n    <color name=\"brown_200\">#BCAAA4</color>\n    <color name=\"brown_300\">#A1887F</color>\n    <color name=\"brown_400\">#8D6E63</color>\n    <color name=\"brown_500\">#795548</color>\n    <color name=\"brown_600\">#6D4C41</color>\n    <color name=\"brown_700\">#5D4037</color>\n    <color name=\"brown_800\">#4E342E</color>\n    <color name=\"brown_900\">#3E2723</color>\n\n    <color name=\"green_50\">#E8F5E9</color>\n    <color name=\"green_100\">#C8E6C9</color>\n    <color name=\"green_200\">#A5D6A7</color>\n    <color name=\"green_300\">#81C784</color>\n    <color name=\"green_400\">#66BB6A</color>\n    <color name=\"green_500\">#4CAF50</color>\n    <color name=\"green_600\">#43A047</color>\n    <color name=\"green_700\">#388E3C</color>\n    <color name=\"green_800\">#2E7D32</color>\n    <color name=\"green_900\">#1B5E20</color>\n    <color name=\"green_A100\">#B9F6CA</color>\n    <color name=\"green_A200\">#69F0AE</color>\n    <color name=\"green_A400\">#00E676</color>\n    <color name=\"green_A700\">#00C853</color>\n\n    <color name=\"purple_200\">#bb86fc</color>\n    <color name=\"purple_600\">#4b01d0</color>\n\n    <color name=\"deep_purple_50\">#EDE7F6</color>\n    <color name=\"deep_purple_100\">#D1C4E9</color>\n    <color name=\"deep_purple_200\">#B39DDB</color>\n    <color name=\"deep_purple_300\">#9575CD</color>\n    <color name=\"deep_purple_400\">#7E57C2</color>\n    <color name=\"deep_purple_500\">#673AB7</color>\n    <color name=\"deep_purple_600\">#5E35B1</color>\n    <color name=\"deep_purple_700\">#512DA8</color>\n    <color name=\"deep_purple_800\">#4527A0</color>\n    <color name=\"deep_purple_900\">#311B92</color>\n    <color name=\"deep_purple_A100\">#B388FF</color>\n    <color name=\"deep_purple_A200\">#7C4DFF</color>\n    <color name=\"deep_purple_A400\">#651FFF</color>\n    <color name=\"deep_purple_A700\">#6200EA</color>\n\n    <color name=\"red_50\">#FFEBEE</color>\n    <color name=\"red_100\">#FFCDD2</color>\n    <color name=\"red_200\">#EF9A9A</color>\n    <color name=\"red_300\">#E57373</color>\n    <color name=\"red_400\">#EF5350</color>\n    <color name=\"red_500\">#F44336</color>\n    <color name=\"red_600\">#E53935</color>\n    <color name=\"red_700\">#D32F2F</color>\n    <color name=\"red_800\">#C62828</color>\n    <color name=\"red_900\">#B71C1C</color>\n    <color name=\"red_A100\">#FF8A80</color>\n    <color name=\"red_A200\">#FF5252</color>\n    <color name=\"red_A400\">#FF1744</color>\n    <color name=\"red_A700\">#D50000</color>\n\n    <color name=\"deep_orange_50\">#FBE9E7</color>\n    <color name=\"deep_orange_100\">#FFCCBC</color>\n    <color name=\"deep_orange_200\">#FFAB91</color>\n    <color name=\"deep_orange_300\">#FF8A65</color>\n    <color name=\"deep_orange_400\">#FF7043</color>\n    <color name=\"deep_orange_500\">#FF5722</color>\n    <color name=\"deep_orange_600\">#F4511E</color>\n    <color name=\"deep_orange_700\">#E64A19</color>\n    <color name=\"deep_orange_800\">#D84315</color>\n    <color name=\"deep_orange_900\">#BF360C</color>\n    <color name=\"deep_orange_A100\">#FF9E80</color>\n    <color name=\"deep_orange_A200\">#FF6E40</color>\n    <color name=\"deep_orange_A400\">#FF3D00</color>\n    <color name=\"deep_orange_A700\">#DD2C00</color>\n\n    <color name=\"yellow_50\">#FFFDE7</color>\n    <color name=\"yellow_100\">#FFF9C4</color>\n    <color name=\"yellow_200\">#FFF59D</color>\n    <color name=\"yellow_300\">#FFF176</color>\n    <color name=\"yellow_400\">#FFEE58</color>\n    <color name=\"yellow_500\">#FFEB3B</color>\n    <color name=\"yellow_600\">#FDD835</color>\n    <color name=\"yellow_700\">#FBC02D</color>\n    <color name=\"yellow_800\">#F9A825</color>\n    <color name=\"yellow_900\">#F57F17</color>\n    <color name=\"yellow_A100\">#FFFF8D</color>\n    <color name=\"yellow_A200\">#FFFF00</color>\n    <color name=\"yellow_A400\">#FFEA00</color>\n    <color name=\"yellow_A700\">#FFD600</color>\n\n    <color name=\"green\">#5CE362</color>\n\n    <!-- Barinsta Theme colors -->\n    <color name=\"barinstaColorPrimary\">#a86735</color>\n    <color name=\"barinstaColorPrimaryLight\">#dd9561</color>\n    <color name=\"barinstaColorPrimaryDark\">#753c0a</color>\n    <color name=\"barinstaColorSecondary\">#d4ab7c</color>\n    <color name=\"barinstaColorSecondaryLight\">#ffddac</color>\n    <color name=\"barinstaColorSecondaryDark\">#a17c4f</color>\n    <color name=\"barinstaPrimaryTextColor\">#616161</color>\n    <!-- <color name=\"barinstaSecondaryTextColor\">@color/white</color> unused -->\n\n    <!-- Bibliogram Theme colors -->\n    <color name=\"bibliogramColorPrimary\">#d63f44</color>\n    <color name=\"bibliogramColorPrimaryLight\">#fff4e8</color>\n    <color name=\"bibliogramColorPrimaryDark\">#8e2929</color>\n    <color name=\"bibliogramColorSecondary\">#d63f44</color>\n    <color name=\"bibliogramColorSecondaryLight\">#fff4e8</color>\n    <color name=\"bibliogramColorSecondaryDark\">#8e2929</color>\n    <color name=\"bibliogramPrimaryTextColor\">#000000</color>\n    <!-- <color name=\"bibliogramSecondaryTextColor\">@color/white</color> unused -->\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"profile_info_container_bottom_space\">8dp</dimen>\n    <dimen name=\"private_page_margins\">30dp</dimen>\n    <dimen name=\"private_page_size\">90dp</dimen>\n\n    <dimen name=\"profile_picture_size\">90dp</dimen>\n    <dimen name=\"profile_chip_size\">40dp</dimen>\n    <dimen name=\"image_size_40\">40dp</dimen>\n\n    <dimen name=\"profile_pic_size_tiny\">24dp</dimen>\n    <dimen name=\"profile_pic_size_smaller\">32dp</dimen>\n    <dimen name=\"profile_pic_size_small\">40dp</dimen>\n    <dimen name=\"profile_pic_size_regular\">48dp</dimen>\n    <dimen name=\"profile_pic_size_large\">90dp</dimen>\n\n    <dimen name=\"slider_item_size\">80dp</dimen>\n    <dimen name=\"highlight_size\">70dp</dimen>\n\n    <dimen name=\"story_item_height\">80dp</dimen>\n    <dimen name=\"story_item_width\">45dp</dimen>\n\n    <dimen name=\"simple_item_picture_size\">80dp</dimen>\n\n    <dimen name=\"notification_image_size\">56dp</dimen>\n\n    <dimen name=\"dm_inbox_avatar_size\">40dp</dimen>\n    <dimen name=\"dm_inbox_avatar_size_small\">30dp</dimen>\n    <dimen name=\"dm_inbox_avatar_size_tiny\">25dp</dimen>\n    <dimen name=\"dm_media_img_max_height\">450dp</dimen>\n    <dimen name=\"dm_link_image_size\">150dp</dimen>\n    <dimen name=\"dm_message_card_radius\">16dp</dimen>\n    <dimen name=\"dm_message_card_radius_small\">8dp</dimen>\n    <dimen name=\"dm_message_item_margin\">80dp</dimen>\n    <dimen name=\"dm_message_item_avatar_size\">48dp</dimen>\n    <dimen name=\"dm_message_info_padding_small\">4dp</dimen>\n    <dimen name=\"dm_reaction_adjust_margin\">24dp</dimen>\n    <dimen name=\"dm_reaction_translation_y_type_1\">6dp</dimen>\n    <dimen name=\"dm_reaction_translation_y_type_2\">-12dp</dimen>\n\n    <dimen name=\"feed_item_bottom_icon_size\">32dp</dimen>\n    <dimen name=\"keyboard_height\">200dp</dimen>\n\n    <dimen name=\"reaction_picker_emoji_size\">40dp</dimen>\n    <dimen name=\"reaction_picker_emoji_margin\">8dp</dimen>\n    <dimen name=\"reaction_picker_option_height\">48dp</dimen>\n    <dimen name=\"reaction_picker_add_padding_adjustment\">4dp</dimen>\n    <dimen name=\"dm_item_context_min_width\">200dp</dimen>\n\n    <dimen name=\"horizontal_divider_height\">1dp</dimen>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/drawables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!--<drawable name=\"exo_styled_controls_play\">@drawable/ic_play_arrow_24</drawable>-->\n    <!--<drawable name=\"exo_styled_controls_pause\">@drawable/ic_pause_24</drawable>-->\n</resources>"
  },
  {
    "path": "app/src/main/res/values/font_certs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <array name=\"com_google_android_gms_fonts_certs\">\n        <item>@array/com_google_android_gms_fonts_certs_dev</item>\n        <item>@array/com_google_android_gms_fonts_certs_prod</item>\n    </array>\n    <string-array name=\"com_google_android_gms_fonts_certs_dev\">\n        <item>\n            MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=\n        </item>\n    </string-array>\n    <string-array name=\"com_google_android_gms_fonts_certs_prod\">\n        <item>\n            MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK\n        </item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"reply\" type=\"id\" />\n    <item name=\"unsend\" type=\"id\" />\n    <item name=\"forward\" type=\"id\" />\n    <item name=\"detail\" type=\"id\" />\n    <item name=\"copy\" type=\"id\" />\n    <item name=\"share_dm\" type=\"id\" />\n    <item name=\"download_current\" type=\"id\" />\n    <item name=\"download_all\" type=\"id\" />\n\n    <item name=\"root_nav_graph\" type=\"id\" />\n\n    <!-- story stickers -->\n    <item name=\"mentions\" type=\"id\" />\n    <item name=\"spotify\" type=\"id\" />\n    <item name=\"poll\" type=\"id\" />\n    <item name=\"question\" type=\"id\" />\n    <item name=\"quiz\" type=\"id\" />\n    <item name=\"slider\" type=\"id\" />\n    <item name=\"viewStoryPost\" type=\"id\" />\n    <item name=\"swipeUp\" type=\"id\" />\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\" translatable=\"false\">Barinsta</string>\n    <string name=\"action_about\">About</string>\n    <string name=\"action_dms\">Direct Messages</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"action_download\">Download</string>\n    <string name=\"action_search\">Search username…</string>\n    <string name=\"action_compare\">Compare</string>\n    <string name=\"clipboard_error\">Error copying text</string>\n    <string name=\"clipboard_copied\">Copied to clipboard!</string>\n    <string name=\"report\">Report</string>\n    <string name=\"set_password\">Protect file with password</string>\n    <string name=\"password_no_max\">Password</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Yes</string>\n    <string name=\"cancel\">Cancel</string>\n    <string name=\"no\">No</string>\n    <string name=\"confirm\">Confirm</string>\n    <string name=\"title_favorites\">Favorites</string>\n    <string name=\"title_discover\">Discover</string>\n    <string name=\"title_comments\">Comments</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Activity</string>\n    <string name=\"update_check\">Check for updates at startup</string>\n    <string name=\"flag_secure\">Block screenshots &amp; app preview</string>\n    <string name=\"download_user_folder\">Download posts to username folders</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Mark stories as seen after viewing</string>\n    <string name=\"mark_as_seen_setting_summary\">Story author will know you viewed it</string>\n    <string name=\"hide_muted_reels_setting\">Hide muted stories from feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Mark DM as seen after viewing</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Other members will know you viewed it</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Enable activity notifications</string>\n    <string name=\"story_sort_setting\">Feed stories sort</string>\n    <string name=\"error_loading_profile\">Error loading profile! Is the username valid? If so, you may be ratelimited.</string>\n    <string name=\"error_loading_hashtag\">Error loading hashtag! Is the name valid?</string>\n    <string name=\"error_loading_location\">Error loading location! Is the URL valid?</string>\n    <string name=\"error_creating_folders\">Error creating Download folder(s).</string>\n    <string name=\"select_folder\">Select folder</string>\n    <string name=\"theme_settings\">Theme</string>\n    <string name=\"select_language\">Language</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPost</item>\n        <item quantity=\"other\">%s\\nPosts</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Post</item>\n        <item quantity=\"other\">%s Posts</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nFollower</item>\n        <item quantity=\"other\">%s\\nFollowers</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nFollowing</string>\n    <string name=\"post_viewer_autoplay_video\">Autoplay videos</string>\n    <string name=\"post_viewer_background_play\">Continue videos in background</string>\n    <string name=\"post_viewer_background_play_summary\">Do not pause videos when the app is out of focus</string>\n    <string name=\"post_viewer_muted_autoplay\">Always mute videos</string>\n    <string name=\"post_viewer_show_captions\">Always show post captions</string>\n    <string name=\"post_viewer_download_dialog_title\">Select what to download</string>\n    <string name=\"post_viewer_download_current\">Current</string>\n    <string name=\"post_viewer_download_album\">Whole Album</string>\n    <string name=\"show_stories\">Show stories</string>\n    <string name=\"no_more_stories\">No more stories!</string>\n    <string name=\"view_post\">View Post</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Answer successful!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Your answer: %s</string>\n    <string name=\"reply_story\">Reply to story</string>\n    <string name=\"reply_hint\">Reply…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Slider</string>\n    <string name=\"story_quizzed\">You have already answered!</string>\n    <string name=\"story_mentions\">Mentions</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">This Account is Private</string>\n    <string name=\"priv_acc_confirm\">You won\\'t be able to access posts after unfollowing! Are you sure?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">You can log in via More -&gt; Account on the bottom-right corner or you can view public accounts without login!</string>\n    <string name=\"empty_acc\">This Account has No Posts</string>\n    <string name=\"empty_list\">No Such Posts!</string>\n    <string name=\"login\">Login</string>\n    <string name=\"logout\">Logout</string>\n    <string name=\"logout_summary\">Browse Instagram anonymously</string>\n    <string name=\"remove_all_acc\">Remove all accounts</string>\n    <string name=\"remove_all_acc_warning\">This will remove all added accounts from the app!\\nTo remove just one account, long tap the account from the account switcher dialog.\\nDo you want to continue?</string>\n    <string name=\"time_settings\">Date format</string>\n    <string name=\"saved_create_collection\">Create new collection</string>\n    <string name=\"edit_collection\">Edit collection name</string>\n    <string name=\"delete_collection\">Delete collection</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Remove from collection</string>\n    <string name=\"liked\">Liked</string>\n    <string name=\"saved\">Saved</string>\n    <string name=\"tagged\">Tagged</string>\n    <string name=\"dm_person\">Message</string>\n    <string name=\"follow\">Follow</string>\n    <string name=\"unfollow\">Unfollow</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorite</string>\n    <string name=\"block\">Block</string>\n    <string name=\"unblock\">Unblock</string>\n    <string name=\"restrict\">Restrict</string>\n    <string name=\"unrestrict\">Unrestrict</string>\n    <string name=\"mute_stories\">Mute stories</string>\n    <string name=\"mute_posts\">Mute posts</string>\n    <string name=\"unmute_stories\">Unmute stories</string>\n    <string name=\"unmute_posts\">Unmute posts</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Copy bio</string>\n    <string name=\"bio_translate\">Translate bio</string>\n    <string name=\"status_mutual\">Mutual</string>\n    <string name=\"status_following\">Following</string>\n    <string name=\"status_follower\">Follower</string>\n    <string name=\"map\">Map</string>\n    <string name=\"dialog_export_accounts\">Accounts</string>\n    <string name=\"dialog_export_settings\">Settings</string>\n    <string name=\"dialog_export_favorites\">Favorites</string>\n    <string name=\"dialog_import_success\">Successfully imported!</string>\n    <string name=\"dialog_import_failed\">Failed to import!</string>\n    <string name=\"dialog_export_success\">Successfully exported!</string>\n    <string name=\"dialog_export_failed\">Failed to export!</string>\n    <string name=\"refresh\">Refresh</string>\n    <string name=\"get_cookies\">Get cookies</string>\n    <string name=\"time_settings_title_custom\">Use custom format</string>\n    <string name=\"time_settings_title_separator\">Separator</string>\n    <string name=\"time_settings_title_time_format\">Time Format</string>\n    <string name=\"time_settings_title_date_format\">Date Format</string>\n    <string name=\"time_settings_title_preview\">Preview</string>\n    <string name=\"time_settings_swap_time\">Swap Time and Date positions</string>\n    <string name=\"quick_access_cannot_delete_curr\">Cannot delete currently in use account</string>\n    <string name=\"quick_access_confirm_delete\">Are you sure you want to delete \\'%s\\'?</string>\n    <string name=\"open_profile\">Open profile</string>\n    <string name=\"view_story\">View story</string>\n    <string name=\"view_pfp\">View profile picture</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Unsupported message type</string>\n    <string name=\"dms_inbox_unsend\">Unsend message</string>\n    <string name=\"dms_inbox_giphy\">View on GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unknown media type</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media expired!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Delivered</string>\n    <string name=\"dms_inbox_raven_media_sent\">Sent</string>\n    <string name=\"dms_inbox_raven_media_opened\">Opened</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Replayed</string>\n    <string name=\"dms_inbox_raven_media_sending\">Sending…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blocked</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggested</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshotted</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Cannot deliver</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Press and hold to record audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Leave chat</string>\n    <string name=\"dms_action_leave_question\">Leave this chat?</string>\n    <string name=\"dms_action_kick\">Kick</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Fetching post(s)</string>\n    <string name=\"downloader_complete\">Download completed</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Downloading post…</string>\n    <string name=\"downloader_downloading_media\">Downloading media</string>\n    <string name=\"downloader_unknown_error\">Unknown error occurred!!!</string>\n    <string name=\"downloader_error_creating_folder\">Error creating folder!</string>\n    <string name=\"downloader_error_download_file\">Error downloading file</string>\n    <string name=\"comment_viewer_translate_comment\">Translate comment</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Followers</string>\n    <string name=\"followers_type_following\">Following</string>\n    <string name=\"followers_compare\">Comparing followers &amp; following</string>\n    <string name=\"followers_both_following\">Both following each other</string>\n    <string name=\"followers_not_following\">not following %s</string>\n    <string name=\"followers_not_follower\">%s is not following</string>\n    <string name=\"login_error_loading_cookies\">Error loading cookies</string>\n    <string name=\"comment_hint\">Write a new comment…</string>\n    <string name=\"liked_notif\">Liked your post</string>\n    <string name=\"comment_notif\">Commented on your post:</string>\n    <string name=\"follow_notif\">Started following you</string>\n    <string name=\"tagged_notif\">Tagged you in a post</string>\n    <string name=\"request_notif\">Requested following you</string>\n    <string name=\"request_approve\">Approve request</string>\n    <string name=\"request_reject\">Reject request</string>\n    <string name=\"share_public_post\">Share this public post to…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">This category is somehow empty…</string>\n    <string name=\"update_available\">An update is available! (%s)</string>\n    <string name=\"updated\">Thank you for updating Barinsta!</string>\n    <string name=\"crash_title\">App crashed</string>\n    <string name=\"crash_descr\">Oops.. the app crashed, but don\\'t worry you can send error report to the developer to help him fix the issue. (:</string>\n    <string name=\"action_notif\">Activity</string>\n    <string name=\"action_archive\">Story archive</string>\n    <string name=\"action_ayml\">Suggested users</string>\n    <string name=\"license\" translatable=\"false\">Copyright (C) 2019-2020 AWAiS\\nCopyright (C) 2020-2021 Austin Huang &amp; Ammar Githam\\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/.</string>\n    <string name=\"liability\" translatable=\"false\">This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d follows</string>\n    <string name=\"activity_count_comments\">%d comments</string>\n    <string name=\"activity_count_commentlikes\">%d comment likes</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d likes</string>\n    <string name=\"activity_count_poy\">%d photos of you</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profile</string>\n    <string name=\"more\">More</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d selected</string>\n    <string name=\"logout_success\">Successfully logged out!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Mark as seen</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Start screen</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">General</string>\n    <string name=\"pref_category_theme\">Theme</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">Account</string>\n    <string name=\"account_hint\">Current login not working? Simply add the account again.</string>\n    <string name=\"add_account\">Add account</string>\n    <string name=\"about_category_license\">License (English only)</string>\n    <string name=\"about_documentation\">Visit our website</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">See our source code on GitHub</string>\n    <string name=\"about_repository_summary\">Audit, star, report bugs, contribute, and have fun (again)!</string>\n    <string name=\"about_feedback\">Send feedback by email</string>\n    <string name=\"about_feedback_summary\" translatable=\"false\">barinsta@austinhuang.me</string>\n    <string name=\"about_category_3pt\">Third-Party Attributions</string>\n    <string name=\"reminder\">Reminder</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">White</string>\n    <string name=\"dark_black_theme\">Black</string>\n    <string name=\"light_theme_settings\">Light theme</string>\n    <string name=\"dark_theme_settings\">Dark theme</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"light_bibliogram_theme\" translatable=\"false\">Bibliogram</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Added to Favorites!</string>\n    <string name=\"add_to_favorites\">Add to Favorites</string>\n    <string name=\"accounts\">Accounts</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Locations</string>\n    <string name=\"unknown\">Unknown</string>\n    <string name=\"removed_from_favs\">Removed from Favourites!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Restore</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">File:</string>\n    <string name=\"enter_password\">Enter password</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">Apply</string>\n    <string name=\"save\">Save</string>\n    <string name=\"caption\">Caption</string>\n    <string name=\"edit_caption\">Edit caption</string>\n    <string name=\"translate_caption\">Translate caption</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"one_x\" translatable=\"false\">1x</string>\n    <string name=\"two_x\" translatable=\"false\">2x</string>\n    <string name=\"pt_two_five_x\" translatable=\"false\">0.25x</string>\n    <string name=\"pt_five_x\" translatable=\"false\">0.5x</string>\n    <string name=\"pt_seven_five_x\" translatable=\"false\">0.75x</string>\n    <string name=\"one_pt_two_five_x\" translatable=\"false\">1.25x</string>\n    <string name=\"one_pt_five_x\" translatable=\"false\">1.5x</string>\n    <string name=\"liking\">Liking…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">Saving…</string>\n    <string name=\"removing\">Removing…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">Downloading…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d of %2$d</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"comment\">Comment</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Share</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Show names</string>\n    <string name=\"show_avatars\">Show avatars</string>\n    <string name=\"avatar_size\">Avatar size</string>\n    <string name=\"corners\">Corners</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">Post not found!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Gallery</string>\n    <string name=\"camera\">Camera</string>\n    <string name=\"all_photos\">All Photos</string>\n    <string name=\"all_media\">All Media</string>\n    <string name=\"all_videos\">All Videos</string>\n    <string name=\"brightness\">Brightness</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exposure</string>\n    <string name=\"center\">Center</string>\n    <string name=\"color\">Color</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">End</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Reset</string>\n    <string name=\"crop\">Crop</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d view</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">Details</string>\n    <string name=\"title\">Title</string>\n    <string name=\"members\">Members</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Inviter</string>\n    <string name=\"mute_messages\">Mute messages</string>\n    <string name=\"mute_mentions\">Mute mentions</string>\n    <string name=\"add_members\">Add members</string>\n    <string name=\"search\">Search</string>\n    <string name=\"done\">Done</string>\n    <string name=\"dms_action_make_admin\">Make Admin</string>\n    <string name=\"dms_action_remove_admin\">Remove as Admin</string>\n    <string name=\"edit_unsuccessful\">Edit was unsuccessful</string>\n    <string name=\"message\">Message</string>\n    <string name=\"tap_to_remove\">Tap to remove</string>\n    <string name=\"forward\">Forward</string>\n    <string name=\"forward_outgoing\">You forwarded a message</string>\n    <string name=\"forward_incoming\">Forwarded a message</string>\n    <string name=\"add\">Add</string>\n    <string name=\"send\">Send</string>\n    <string name=\"replying_to_yourself\">Replying to yourself</string>\n    <string name=\"replying_to_user\">Replying to %s</string>\n    <string name=\"replied_to_yourself\">You replied to yourself</string>\n    <string name=\"replied_you\">You replied</string>\n    <string name=\"replied_you_group\">You replied to %s</string>\n    <string name=\"replied_group\">Replied to %s</string>\n    <string name=\"replied_to_you\">Replied to you</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">You reacted to their story</string>\n    <string name=\"reacted_story_incoming\">Reacted to your story</string>\n    <string name=\"mentioned_story_outgoing\">You mentioned them in your story</string>\n    <string name=\"mentioned_story_incoming\">Mentioned you in their story</string>\n    <string name=\"replied_story_outgoing\">You replied to their story</string>\n    <string name=\"replied_story_incoming\">Replied to your story</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">@%s\\'s story</string>\n    <string name=\"story_share_highlight\">@%s\\'s story highlight</string>\n    <string name=\"photo\">Photo</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Voice message</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">Approval required to join</string>\n    <string name=\"requests\">Requests</string>\n    <string name=\"admins_only\">Admins only</string>\n    <string name=\"added_by\">Added by %s</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Decline</string>\n    <string name=\"accept\">Accept</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Notifications</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">Enable DM notifications</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">secs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Search GIPHY</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\">Learn more.&lt;/a></string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More > Settings > Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <!--\n    <style name=\"AppTheme.LaunchTheme\" parent=\"AppTheme\">\n        <item name=\"android:windowBackground\">@drawable/launch</item>\n    </style>\n    -->\n\n    <style name=\"Theme.AppCompat.Translucent\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowAnimationStyle\">@android:style/Animation</item>\n    </style>\n\n    <style name=\"PlayButtonCard\" parent=\"CardView.Dark\" />\n\n    <!--\n    <style name=\"FlyingGayDialog\" parent=\"Theme.MaterialComponents.DayNight.Dialog.FixedSize\">\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowActionBar\">false</item>\n    </style>\n    -->\n\n    <!--<style name=\"AppTheme.BottomSheetDialog\" parent=\"Theme.MaterialComponents.Light.BottomSheetDialog\" />-->\n\n    <!--<style name=\"AppTheme.WindowAnimationTransition\">-->\n    <!--    <item name=\"android:windowEnterAnimation\">@android:anim/fade_in</item>-->\n    <!--    <item name=\"android:windowExitAnimation\">@android:anim/fade_out</item>-->\n    <!--</style>-->\n\n    <style name=\"AppTheme.BottomNavigationView\" parent=\"@style/Widget.MaterialComponents.BottomNavigationView.PrimarySurface\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.App.BottomNavigationView</item>\n    </style>\n\n    <style name=\"ThemeOverlay.App.BottomNavigationView\" parent=\"\" />\n\n    <style name=\"Widget.AppTheme.Toolbar.PrimarySurface\" parent=\"Widget.MaterialComponents.Toolbar.PrimarySurface\" />\n\n    <!--<style name=\"ThemeOverlay.AppTheme.Dark.ActionBar\" parent=\"ThemeOverlay.MaterialComponents.Dark.ActionBar\" />-->\n\n    <style name=\"Widget.App.ActionMode\" parent=\"Widget.AppCompat.ActionMode\">\n        <item name=\"background\">?attr/colorPrimary</item>\n    </style>\n\n    <style name=\"Widget.BottomNavigationView.Light.White\" parent=\"@style/Widget.MaterialComponents.BottomNavigationView.Colored\">\n        <item name=\"colorPrimary\">@color/black</item>\n    </style>\n\n    <style name=\"Widget.BottomNavigationView.Dark.Black\" parent=\"@style/Widget.MaterialComponents.BottomNavigationView.Colored\">\n        <item name=\"colorPrimary\">@color/white</item>\n    </style>\n\n    <style name=\"Widget.BottomNavigationView.Light.Barinsta\" parent=\"@style/Widget.MaterialComponents.BottomNavigationView.Colored\">\n        <item name=\"colorPrimary\">@color/barinstaColorPrimary</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Button.Light.White\" parent=\"Widget.MaterialComponents.Button\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.Button.Light.White</item>\n    </style>\n\n    <style name=\"ThemeOverlay.Button.Light.White\" parent=\"\">\n        <item name=\"colorPrimary\">@color/black</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Button.Dark.Black\" parent=\"Widget.MaterialComponents.Button\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.Button.Dark.Black</item>\n    </style>\n\n    <style name=\"ThemeOverlay.Button.Dark.Black\" parent=\"\">\n        <item name=\"colorPrimary\">@color/white</item>\n    </style>\n\n    <style name=\"Widget.Dialog.Dark.Black\" parent=\"@style/Theme.AppCompat.Dialog\">\n        <item name=\"android:background\">@color/grey_800</item>\n    </style>\n\n    <style name=\"Widget.AlertDialog.Dark.Black\" parent=\"@style/Theme.AppCompat.Dialog.Alert\">\n        <item name=\"android:background\">@color/grey_800</item>\n    </style>\n\n    <style name=\"Widget.AppCompat.CompoundButton.Switch.Dark.Black\" parent=\"@style/Widget.AppCompat.CompoundButton.Switch\">\n        <item name=\"trackTint\">@color/grey_800</item>\n        <item name=\"colorControlActivated\">@color/blue_900</item>\n    </style>\n\n    <style name=\"ThemeOverlay.Switch.Dark.Black\" parent=\"\">\n        <item name=\"colorOnSurface\">@color/grey_800</item>\n    </style>\n\n    <style name=\"Widget.AppCompat.ListView.DropDown.Dark.Black\" parent=\"Widget.AppCompat.ListView.DropDown\">\n        <item name=\"android:background\">@color/grey_800</item>\n    </style>\n\n    <style name=\"PreferenceFragmentCompatStyle.Dark.Black\" parent=\"PreferenceFragment.Material\">\n        <item name=\"android:divider\">@drawable/pref_list_divider_material</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Toolbar.Light.Barinsta\" parent=\"Widget.MaterialComponents.Toolbar.Primary\">\n        <item name=\"android:textColorPrimary\">@color/white</item>\n        <item name=\"colorControlNormal\">@color/white</item>\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.MaterialComponents.ActionBar.Light.Barinsta</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.ActionBar.Light.Barinsta\" parent=\"ThemeOverlay.MaterialComponents.ActionBar.Primary\">\n        <item name=\"colorControlNormal\">@color/white</item>\n        <item name=\"android:textColorHint\">@color/brown_600</item>\n    </style>\n\n    <style name=\"PreferenceFragmentCompatStyle.Dark.MaterialDark\" parent=\"PreferenceFragment.Material\">\n        <item name=\"android:background\">?colorSurface</item>\n        <item name=\"android:windowBackground\">?attr/colorSurface</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.MaterialAlertDialog.Light\" parent=\"ThemeOverlay.MaterialComponents.MaterialAlertDialog\">\n        <item name=\"buttonBarPositiveButtonStyle\">@style/Widget.MaterialComponents.Button.TextButton.Dialog.Light</item>\n        <item name=\"buttonBarNeutralButtonStyle\">@style/Widget.MaterialComponents.Button.TextButton.Dialog.Light</item>\n        <item name=\"buttonBarNegativeButtonStyle\">@style/Widget.MaterialComponents.Button.TextButton.Dialog.Light</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Button.TextButton.Dialog.Light\" parent=\"Widget.MaterialComponents.Button.TextButton.Dialog\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.MaterialComponents.Button.TextButton.Light</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.Button.TextButton.Light\" parent=\"\">\n        <item name=\"colorPrimary\">?attr/colorPrimaryDark</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.White\" parent=\"Widget.MaterialComponents.TextInputLayout.OutlinedBox\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Light.White</item>\n        <item name=\"colorPrimary\">@color/black</item>\n        <item name=\"colorOnSurface\">@color/black</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Light.White\" parent=\"ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox\">\n        <item name=\"colorPrimary\">@color/black</item>\n        <item name=\"colorOnSurface\">@color/black</item>\n    </style>\n\n    <style name=\"Widget.App.Button.OutlinedButton.IconOnly\" parent=\"Widget.MaterialComponents.Button.OutlinedButton\">\n        <item name=\"iconPadding\">0dp</item>\n        <item name=\"android:insetTop\">0dp</item>\n        <item name=\"android:insetBottom\">0dp</item>\n        <item name=\"android:paddingLeft\">12dp</item>\n        <item name=\"android:paddingRight\">12dp</item>\n        <item name=\"android:minWidth\">48dp</item>\n        <item name=\"android:minHeight\">48dp</item>\n    </style>\n\n    <style name=\"Widget.App.MaterialButton.IconOnly\" parent=\"Widget.MaterialComponents.Button.OutlinedButton\">\n        <item name=\"iconGravity\">textStart</item>\n        <item name=\"iconPadding\">0dp</item>\n        <item name=\"android:insetTop\">0dp</item>\n        <item name=\"android:insetBottom\">0dp</item>\n        <item name=\"android:insetLeft\">0dp</item>\n        <item name=\"android:insetRight\">0dp</item>\n    </style>\n\n    <style name=\"Widget.App.MaterialButton.IconOnly.BorderlessRipple\" parent=\"Widget.App.MaterialButton.IconOnly\">\n        <item name=\"android:background\">@drawable/background_grey_ripple</item>\n    </style>\n\n    <style name=\"dialog_window_animation\">\n        <item name=\"android:windowEnterAnimation\">@anim/dialog_anim_in</item>\n        <item name=\"android:windowExitAnimation\">@anim/dialog_anim_out</item>\n    </style>\n\n    <style name=\"Widget.AppCompat.ListView.DropDown.Exoplayer\" parent=\"Widget.AppCompat.ListView.DropDown\">\n        <item name=\"divider\">@color/grey_800</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.PopupMenu.Exoplayer\" parent=\"Widget.MaterialComponents.PopupMenu\">\n        <item name=\"popupMenuBackground\">@drawable/popup_background_exoplayer</item>\n        <item name=\"divider\">@color/grey_700</item>\n        <item name=\"dividerHorizontal\">@color/grey_700</item>\n    </style>\n\n    <style name=\"popupMenuStyle\">\n        <item name=\"popupMenuBackground\">@drawable/popup_background_exoplayer</item>\n        <item name=\"android:textColor\">@color/white</item>\n        <item name=\"android:divider\">@drawable/pref_list_divider_material</item>\n    </style>\n\n    <style name=\"TextAppearance.MaterialComponents.Tooltip.Exoplayer\" parent=\"TextAppearance.MaterialComponents.Tooltip\">\n        <item name=\"android:textColor\">@color/white</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Tooltip.ExoPlayer\" parent=\"Widget.MaterialComponents.Tooltip\">\n        <item name=\"android:textAppearance\">@style/TextAppearance.MaterialComponents.Tooltip.Exoplayer</item>\n    </style>\n\n    <style name=\"PostViewV2Style\" parent=\"android:Theme.Translucent\">\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"android:windowLightNavigationBar\" tools:targetApi=\"o_mr1\">false</item>\n        <item name=\"android:navigationBarColor\">@color/black</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n    </style>\n\n    <style name=\"ShapeAppearanceOverlay.App.Button.Circle\" parent=\"\">\n        <item name=\"cornerFamily\">rounded</item>\n        <item name=\"cornerSize\">50%</item>\n    </style>\n\n    <style name=\"ShapeAppearanceOverlay.Rounded\" parent=\"\">\n        <item name=\"cornerSize\">8dp</item>\n        <item name=\"cornerFamily\">rounded</item>\n    </style>\n\n    <style name=\"ThemeOverlay.Rounded.BottomSheetDialog\" parent=\"@style/ThemeOverlay.MaterialComponents.BottomSheetDialog\">\n        <item name=\"bottomSheetStyle\">@style/Widget.Rounded.BottomSheet</item>\n    </style>\n\n    <style name=\"Widget.Rounded.BottomSheet\" parent=\"Widget.MaterialComponents.BottomSheet\">\n        <item name=\"shapeAppearanceOverlay\">@style/ShapeAppearanceOverlay.Rounded</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.Button.Icon.NoInsets\" parent=\"Widget.MaterialComponents.Button.Icon\">\n        <item name=\"android:insetLeft\">1px</item>\n        <item name=\"android:insetTop\">0dp</item>\n        <item name=\"android:insetRight\">0dp</item>\n        <item name=\"android:insetBottom\">0dp</item>\n        <item name=\"android:paddingStart\">0dp</item>\n        <item name=\"android:paddingEnd\">0dp</item>\n        <item name=\"iconPadding\">0dp</item>\n    </style>\n\n    <style name=\"TextAppearance.Design.Tab.RegularCaps\" parent=\"TextAppearance.Design.Tab\">\n        <item name=\"textAllCaps\">false</item>\n        <item name=\"android:textAllCaps\">false</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TabLayout.RegularCaps\" parent=\"Widget.MaterialComponents.TabLayout\">\n        <item name=\"tabTextAppearance\">@style/TextAppearance.Design.Tab.RegularCaps</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TabLayout.Light.White\" parent=\"Widget.MaterialComponents.TabLayout\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.App.TabLayout.Light.White</item>\n    </style>\n\n    <style name=\"ThemeOverlay.App.TabLayout.Light.White\" parent=\"\">\n        <item name=\"colorPrimary\">@color/black</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TabLayout.Dark.Black\" parent=\"Widget.MaterialComponents.TabLayout\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.App.TabLayout.Dark.Black</item>\n    </style>\n\n    <style name=\"ThemeOverlay.App.TabLayout.Dark.Black\" parent=\"\">\n        <item name=\"colorPrimary\">@color/white</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.Barinsta\" parent=\"Widget.MaterialComponents.TextInputLayout.OutlinedBox\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Light.Barinsta</item>\n        <item name=\"colorPrimary\">@color/black</item>\n        <item name=\"colorOnSurface\">@color/black</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox.Light.Barinsta\" parent=\"ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox\">\n        <item name=\"colorPrimary\">@color/white</item>\n    </style>\n\n    <style name=\"Widget.MaterialComponents.TextInputLayout.FilledBox.Dark.Black\" parent=\"Widget.MaterialComponents.TextInputLayout.FilledBox\">\n        <item name=\"materialThemeOverlay\">@style/ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox.Dark.Black</item>\n        <item name=\"hintTextColor\">@color/white</item>\n        <item name=\"boxStrokeColor\">@color/white</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox.Dark.Black\" parent=\"ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox\">\n        <item name=\"colorPrimary\">@color/white</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <style name=\"AppTheme\" parent=\"\" />\n\n    <!-- Light Themes -->\n    <style name=\"AppTheme.Light\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n        <!-- Common start -->\n        <item name=\"android:statusBarColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"android:navigationBarColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"android:navigationBarDividerColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"windowActionModeOverlay\">true</item>\n        <item name=\"actionModeCloseDrawable\">@drawable/ic_close_24</item>\n        <item name=\"android:textColorLink\">@color/blue_700</item>\n        <item name=\"android:textColorHighlight\">@color/blue_300</item>\n        <item name=\"actionModeStyle\">@style/Widget.App.ActionMode</item>\n        <!-- Common end -->\n        <item name=\"android:windowLightStatusBar\" tools:ignore=\"NewApi\">false</item>\n        <item name=\"android:windowLightNavigationBar\" tools:ignore=\"NewApi\">false</item>\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialComponents.ActionBar</item>\n        <!--<item name=\"toolbarStyle\">@style/Widget.MaterialComponents.Toolbar.Primary</item>-->\n        <item name=\"materialAlertDialogTheme\">@style/ThemeOverlay.MaterialComponents.MaterialAlertDialog.Light</item>\n        <item name=\"dmIncomingBgColor\">@color/grey_600</item>\n        <item name=\"dmOutgoingBgColor\">@color/deep_purple_400</item>\n        <item name=\"dmDateHeaderBgColor\">@color/deep_purple_600</item>\n        <item name=\"searchInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>\n    </style>\n\n    <style name=\"AppTheme.Light.White\" parent=\"AppTheme.Light\">\n        <item name=\"colorPrimary\">@color/white</item>\n        <item name=\"colorPrimaryDark\">@color/black</item>\n        <item name=\"colorOnPrimary\">@color/black</item>\n        <item name=\"colorSecondary\">@color/white</item>\n        <item name=\"colorSecondaryVariant\">@color/white</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <item name=\"colorSurface\">@color/white</item>\n        <item name=\"toolbarColor\">?colorSurface</item>\n        <item name=\"colorOnSurface\">@color/black</item>\n        <item name=\"colorAccent\">@color/black</item>\n        <item name=\"colorControlActivated\">@color/black</item>\n        <item name=\"android:colorControlActivated\">@color/black</item>\n        <item name=\"editTextColor\">@color/black</item>\n        <item name=\"android:editTextColor\">@color/black</item>\n        <item name=\"android:textColorPrimary\">@color/black</item>\n        <item name=\"android:windowBackground\">@color/white</item>\n        <item name=\"bottomNavigationStyle\">@style/Widget.BottomNavigationView.Light.White</item>\n        <item name=\"materialButtonStyle\">@style/Widget.MaterialComponents.Button.Light.White</item>\n        <item name=\"textInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.White</item>\n        <item name=\"dmIncomingBgColor\">@color/grey_600</item>\n        <item name=\"dmOutgoingBgColor\">@color/deep_purple_400</item>\n        <item name=\"dmDateHeaderBgColor\">@color/deep_purple_600</item>\n        <item name=\"dmWaveformBgColor\">@color/grey_600</item>\n        <item name=\"dmWaveformProgressColor\">@color/blue_800</item>\n        <item name=\"dmInputTextColor\">@color/black</item>\n        <item name=\"tabStyle\">@style/Widget.MaterialComponents.TabLayout.Light.White</item>\n        <item name=\"parentCommentHighlightColor\">@color/parent_comment_light_white</item>\n    </style>\n\n    <style name=\"AppTheme.Light.Barinsta\" parent=\"AppTheme.Light\">\n        <item name=\"colorPrimary\">@color/barinstaColorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/barinstaColorPrimaryDark</item>\n        <item name=\"colorPrimaryVariant\">@color/barinstaColorPrimaryLight</item>\n        <item name=\"colorSecondary\">@color/barinstaColorSecondary</item>\n        <item name=\"colorSecondaryVariant\">@color/barinstaColorSecondaryDark</item>\n        <item name=\"colorSurface\">@color/grey_200</item>\n        <item name=\"toolbarColor\">?colorPrimary</item>\n        <item name=\"colorControlHighlight\">@color/barinstaColorSecondaryDark</item>\n        <item name=\"colorAccent\">@color/barinstaColorSecondaryDark</item>\n        <!--<item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialComponents.ActionBar</item>-->\n        <item name=\"android:windowBackground\">?colorSurface</item>\n        <item name=\"bottomNavigationStyle\">@style/Widget.BottomNavigationView.Light.Barinsta</item>\n        <item name=\"android:textColorPrimary\">@color/barinstaPrimaryTextColor</item>\n        <item name=\"toolbarStyle\">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>\n        <item name=\"dmInputTextColor\">@color/black</item>\n        <item name=\"searchInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.Barinsta</item>\n        <item name=\"parentCommentHighlightColor\">@color/parent_comment_light_white</item>\n    </style>\n\n    <style name=\"AppTheme.Light.Bibliogram\" parent=\"AppTheme.Light\">\n        <item name=\"colorPrimary\">@color/bibliogramColorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/bibliogramColorPrimaryDark</item>\n        <item name=\"colorPrimaryVariant\">@color/bibliogramColorPrimaryLight</item>\n        <item name=\"colorSecondary\">@color/bibliogramColorSecondary</item>\n        <item name=\"colorSecondaryVariant\">@color/bibliogramColorSecondaryDark</item>\n        <item name=\"colorSurface\">@color/grey_200</item>\n        <item name=\"toolbarColor\">?colorPrimary</item>\n        <item name=\"colorControlHighlight\">@color/bibliogramColorSecondaryDark</item>\n        <item name=\"colorAccent\">@color/bibliogramColorSecondaryDark</item>\n        <item name=\"android:windowBackground\">?colorSurface</item>\n        <item name=\"bottomNavigationStyle\">@style/Widget.BottomNavigationView.Light.Barinsta</item>\n        <item name=\"android:textColorPrimary\">@color/bibliogramPrimaryTextColor</item>\n        <item name=\"toolbarStyle\">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>\n        <item name=\"dmInputTextColor\">@color/black</item>\n        <item name=\"searchInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Light.Barinsta</item>\n        <item name=\"parentCommentHighlightColor\">@color/parent_comment_light_white</item>\n    </style>\n\n\n    <!-- Dark Themes -->\n    <style name=\"AppTheme.Dark\" parent=\"Theme.MaterialComponents.NoActionBar\">\n        <!-- Common start -->\n        <item name=\"android:statusBarColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"android:navigationBarColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"android:navigationBarDividerColor\" tools:ignore=\"NewApi\">?attr/colorPrimaryDark</item>\n        <item name=\"windowActionModeOverlay\">true</item>\n        <item name=\"actionModeCloseDrawable\">@drawable/ic_close_24</item>\n        <item name=\"android:textColorLink\">@color/blue_700</item>\n        <item name=\"android:textColorHighlight\">@color/blue_300</item>\n        <item name=\"actionModeStyle\">@style/Widget.App.ActionMode</item>\n        <!-- Common end -->\n        <item name=\"android:windowLightStatusBar\" tools:ignore=\"NewApi\">false</item>\n        <item name=\"android:windowLightNavigationBar\" tools:ignore=\"NewApi\">false</item>\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>\n        <!--<item name=\"toolbarStyle\">@style/Widget.MaterialComponents.Toolbar.Primary</item>-->\n        <item name=\"dmWaveformBgColor\">@color/white</item>\n        <item name=\"dmWaveformProgressColor\">@color/blue_800</item>\n        <item name=\"dmInputTextColor\">@color/white</item>\n        <item name=\"searchInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>\n    </style>\n\n    <style name=\"AppTheme.Dark.Black\" parent=\"AppTheme.Dark\">\n        <item name=\"android:windowLightStatusBar\" tools:ignore=\"NewApi\">false</item>\n        <item name=\"colorPrimary\">@color/black</item>\n        <item name=\"colorPrimaryDark\">@color/black</item>\n        <item name=\"colorOnPrimary\">@color/white</item>\n        <item name=\"colorSecondary\">@color/black</item>\n        <item name=\"colorOnSecondary\">@color/white</item>\n        <item name=\"colorSecondaryVariant\">@color/black</item>\n        <item name=\"colorSurface\">@color/black</item>\n        <item name=\"toolbarColor\">?colorSurface</item>\n        <item name=\"colorOnSurface\">@color/white</item>\n        <item name=\"colorAccent\">@color/blue_A700</item>\n        <item name=\"colorControlHighlight\">@color/grey_600</item>\n        <item name=\"colorControlActivated\">@color/white</item>\n        <item name=\"android:colorControlActivated\">@color/white</item>\n        <item name=\"android:textColorPrimary\">@color/white</item>\n        <item name=\"android:textColorSecondary\">@color/white</item>\n        <item name=\"android:textColorHint\">@color/grey_500</item>\n        <item name=\"android:windowBackground\">@color/black</item>\n        <item name=\"bottomNavigationStyle\">@style/Widget.BottomNavigationView.Dark.Black</item>\n        <item name=\"materialButtonStyle\">@style/Widget.MaterialComponents.Button.Dark.Black</item>\n        <item name=\"android:dialogTheme\">@style/Widget.Dialog.Dark.Black</item>\n        <item name=\"alertDialogTheme\">@style/Widget.AlertDialog.Dark.Black</item>\n        <item name=\"switchStyle\">@style/Widget.AppCompat.CompoundButton.Switch.Dark.Black</item>\n        <!--<item name=\"android:dropDownListViewStyle\">@style/Widget.AppCompat.ListView.DropDown.Dark.Black</item>-->\n        <item name=\"preferenceFragmentCompatStyle\">@style/PreferenceFragmentCompatStyle.Dark.Black</item>\n        <item name=\"appBarLayoutStyle\">@style/Widget.MaterialComponents.AppBarLayout.Primary</item>\n        <item name=\"dmIncomingBgColor\">@color/grey_600</item>\n        <item name=\"dmOutgoingBgColor\">@color/deep_purple_400</item>\n        <item name=\"dmDateHeaderBgColor\">@color/deep_purple_600</item>\n        <item name=\"tabStyle\">@style/Widget.MaterialComponents.TabLayout.Dark.Black</item>\n        <item name=\"textInputStyle\">@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dark.Black</item>\n        <item name=\"parentCommentHighlightColor\">@color/parent_comment_dark_materialdark</item>\n    </style>\n\n    <style name=\"AppTheme.Dark.MaterialDark\" parent=\"AppTheme.Dark\">\n        <item name=\"colorPrimary\">@color/purple_200</item>\n        <item name=\"colorPrimaryVariant\">@color/purple_600</item>\n        <item name=\"colorSecondary\">@color/green_500</item>\n        <item name=\"colorSecondaryVariant\">@color/green_200</item>\n        <item name=\"android:windowBackground\">@color/grey_900</item>\n        <item name=\"android:colorBackground\">@color/grey_900</item>\n        <item name=\"colorSurface\">@color/grey_900</item>\n        <item name=\"toolbarColor\">?colorSurface</item>\n        <item name=\"colorError\">@color/red_200</item>\n        <item name=\"colorOnPrimary\">@color/black</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <item name=\"colorOnBackground\">@color/white</item>\n        <item name=\"colorOnSurface\">@color/white</item>\n        <item name=\"colorOnError\">@color/black</item>\n        <item name=\"materialAlertDialogTheme\">@style/ThemeOverlay.MaterialComponents.Dialog.Alert</item>\n        <item name=\"preferenceFragmentCompatStyle\">@style/PreferenceFragmentCompatStyle.Dark.MaterialDark</item>\n        <item name=\"dmIncomingBgColor\">@color/grey_600</item>\n        <item name=\"dmOutgoingBgColor\">@color/deep_purple_400</item>\n        <item name=\"dmDateHeaderBgColor\">@color/deep_purple_600</item>\n        <item name=\"parentCommentHighlightColor\">@color/parent_comment_dark_materialdark</item>\n    </style>\n\n    <style name=\"AppTheme.Launcher\" parent=\"AppTheme.Dark\">\n        <item name=\"android:windowBackground\">@drawable/launch_screen</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-ar/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>الوضع الافتراضي للنظام</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>تلقائيًا / أتبع النظام</item>\n        <item>تلقائيًا / أتبع البطارية</item>\n        <item>مظلم</item>\n        <item>فاتح</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>إنستجرام افتراضي (غير مقروء ثم مقروء)</item>\n        <item>من الأحدث إلى الأقدم</item>\n        <item>من الأقدم إلى الأحدث</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>لا شيء</item>\n        <item>\\@</item>\n        <item>في</item>\n        <item>على</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>ثواني</item>\n        <item>دقائق</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">عن التطبيق</string>\n    <string name=\"action_dms\">الرسائل الخاصة</string>\n    <string name=\"action_settings\">الإعدادات</string>\n    <string name=\"action_download\">تحميل</string>\n    <string name=\"action_search\">البحث عن اسم المستخدم…</string>\n    <string name=\"action_compare\">مقارنة</string>\n    <string name=\"clipboard_error\">حدث خطأ أثناء نسخ النص</string>\n    <string name=\"clipboard_copied\">تم النسخ إلى الحافظة!</string>\n    <string name=\"report\">تقرير</string>\n    <string name=\"set_password\">حماية الملف بكلمة المرور</string>\n    <string name=\"password_no_max\">كلمة المرور</string>\n    <string name=\"ok\">حسنا</string>\n    <string name=\"yes\">نعم</string>\n    <string name=\"cancel\">إلغاء</string>\n    <string name=\"no\">لا</string>\n    <string name=\"confirm\">تأكيد</string>\n    <string name=\"title_favorites\">المفضلة</string>\n    <string name=\"title_discover\">إكتشف</string>\n    <string name=\"title_comments\">التعليقات</string>\n    <string name=\"title_replies\">الردود</string>\n    <string name=\"title_notifications\">الأنشطة</string>\n    <string name=\"update_check\">التحقق من التحديثات عند بدء التشغيل</string>\n    <string name=\"flag_secure\">منع لقطات الشاشة &amp; معاينة التطبيق</string>\n    <string name=\"download_user_folder\">تنزيل المشاركات إلى مجلدات بأسم المستخدم</string>\n    <string name=\"download_prepend_username\">إضافة اسم المستخدم إلى اسم الملف</string>\n    <string name=\"mark_as_seen_setting\">ضع علامة على القصص كما شاهدتها بعد مشاهدتها</string>\n    <string name=\"mark_as_seen_setting_summary\">مؤلف القصة سيعرف أنك شاهدته</string>\n    <string name=\"hide_muted_reels_setting\">إخفاء القصص المكتومة من الخلاصة</string>\n    <string name=\"dm_mark_as_seen_setting\">تحديد DM كما هو بعد المشاهدة</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">الأعضاء الآخرون سيعلمون أنك شاهدتها</string>\n    <string name=\"autoplay_stories_setting\">تشغيل تلقائي لقصص الفيديو</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">تمكين تنبيهات النشاط</string>\n    <string name=\"story_sort_setting\">تصنيف قصص التغذية</string>\n    <string name=\"error_loading_profile\">خطأ في تحميل الملف الشخصي! هل اسم المستخدم صحيح؟ إذا كان الأمر كذلك، قد تكون مقيدا.</string>\n    <string name=\"error_loading_hashtag\">خطأ في تحميل الهاشتاق! هل الاسم متاح؟</string>\n    <string name=\"error_loading_location\">خطأ في تحميل الموقع! هل عنوان URL صحيح؟</string>\n    <string name=\"error_creating_folders\">خطأ في إنشاء مجلد(ات) التنزيل.</string>\n    <string name=\"select_folder\">تحديد مجلد</string>\n    <string name=\"theme_settings\">السمة</string>\n    <string name=\"select_language\">اللغة</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"zero\">%s\\nPosts</item>\n        <item quantity=\"one\">%s\\nPost</item>\n        <item quantity=\"two\">%s\\nPosts</item>\n        <item quantity=\"few\">%s\\nPosts</item>\n        <item quantity=\"many\">%s\\nPosts</item>\n        <item quantity=\"other\">%s\\nPosts</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"zero\">%s Posts</item>\n        <item quantity=\"one\">%s Post</item>\n        <item quantity=\"two\">%s Posts</item>\n        <item quantity=\"few\">%s Posts</item>\n        <item quantity=\"many\">%s Posts</item>\n        <item quantity=\"other\">%s Posts</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"zero\">%s\\nFollowers</item>\n        <item quantity=\"one\">%s\\nFollower</item>\n        <item quantity=\"two\">%s\\nFollowers</item>\n        <item quantity=\"few\">%s\\nFollowers</item>\n        <item quantity=\"many\">%s\\nFollowers</item>\n        <item quantity=\"other\">%s\\nFollowers</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nمتابع</string>\n    <string name=\"post_viewer_autoplay_video\">تشغيل مقاطع الفيديو تلقائياً</string>\n    <string name=\"post_viewer_background_play\">متابعة الفيديوهات في الخلفية</string>\n    <string name=\"post_viewer_background_play_summary\">لا تقم بإيقاف الفيديوهات مؤقتاً عندما يكون التطبيق خارج نطاق التركيز</string>\n    <string name=\"post_viewer_muted_autoplay\">كتم الفيديوهات دائماً</string>\n    <string name=\"post_viewer_show_captions\">إظهار دائما تسميات المنشور</string>\n    <string name=\"post_viewer_download_dialog_title\">حدد ما تريد تحميله</string>\n    <string name=\"post_viewer_download_current\">الحالي</string>\n    <string name=\"post_viewer_download_album\">الألبوم بأكمله</string>\n    <string name=\"show_stories\">إظهار القصص</string>\n    <string name=\"no_more_stories\">لا توجد قصص أخرى!</string>\n    <string name=\"view_post\">عرض المشاركة</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">تم الرد بنجاح!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"zero\">%d responses averaging %s</item>\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"two\">%d responses averaging %s</item>\n        <item quantity=\"few\">%d responses averaging %s</item>\n        <item quantity=\"many\">%d responses averaging %s</item>\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">إجابتك: %s</string>\n    <string name=\"reply_story\">رد على قصتك</string>\n    <string name=\"reply_hint\">رد…</string>\n    <string name=\"story_quiz\">اختبار قصير</string>\n    <string name=\"story_slider\">شريط منزلق</string>\n    <string name=\"story_quizzed\">لقد قمت بالرد مسبقا!</string>\n    <string name=\"story_mentions\">الإشارات</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">هذا الحساب خاص</string>\n    <string name=\"priv_acc_confirm\">لن تتمكن من الوصول إلى المشاركات بعد إلغاء المتابعة! هل أنت متأكد؟</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">يمكنك تسجيل الدخول عبر المزيد -&gt; الحساب على الزاوية اليمنى السفلى أو يمكنك عرض الحسابات العامة بدون تسجيل الدخول!</string>\n    <string name=\"empty_acc\">هذا الحساب لا يتضمن مشاركات</string>\n    <string name=\"empty_list\">لا توجد مثل هذه المشاركات!</string>\n    <string name=\"login\">تسجيل الدخول</string>\n    <string name=\"logout\">تسجيل الخروج</string>\n    <string name=\"logout_summary\">تصفح Instagram مجهول الهوية</string>\n    <string name=\"remove_all_acc\">أزل كل الحسابات</string>\n    <string name=\"remove_all_acc_warning\">سيؤدي هذا إلى إزالة جميع الحسابات المضافة من التطبيق!\\nلإزالة حساب واحد فقط، انقر طويلا على الحساب من مربع حوار تبديل الحساب.\\nهل تريد المتابعة؟</string>\n    <string name=\"time_settings\">تنسيق التاريخ</string>\n    <string name=\"saved_create_collection\">إنشاء مجموعة جديدة</string>\n    <string name=\"edit_collection\">تحرير اسم المجموعة</string>\n    <string name=\"delete_collection\">حذف المجموعة</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">إزالة من المجموعة</string>\n    <string name=\"liked\">أعجبني</string>\n    <string name=\"saved\">تم الحفظ</string>\n    <string name=\"tagged\">مشار إليها</string>\n    <string name=\"dm_person\">الرسالة</string>\n    <string name=\"follow\">تابع</string>\n    <string name=\"unfollow\">إلغاء المتابعة</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">المفضلة</string>\n    <string name=\"block\">حظر</string>\n    <string name=\"unblock\">إلغاء الحظر</string>\n    <string name=\"restrict\">تقييد</string>\n    <string name=\"unrestrict\">إلغاء القيود</string>\n    <string name=\"mute_stories\">كتم القصص</string>\n    <string name=\"mute_posts\">كتم المشاركات</string>\n    <string name=\"unmute_stories\">إلغاء كتم القصص</string>\n    <string name=\"unmute_posts\">إلغاء كتم المنشورات</string>\n    <string name=\"remove_follower\">إزالة متابع</string>\n    <string name=\"bio_copy\">نسخ البيانات</string>\n    <string name=\"bio_translate\">Translate bio</string>\n    <string name=\"status_mutual\">Mutual</string>\n    <string name=\"status_following\">متابع</string>\n    <string name=\"status_follower\">المتابعون</string>\n    <string name=\"map\">خريطه</string>\n    <string name=\"dialog_export_accounts\">الحسابات</string>\n    <string name=\"dialog_export_settings\">الإعدادات</string>\n    <string name=\"dialog_export_favorites\">المفضلة</string>\n    <string name=\"dialog_import_success\">تمّ الإستيراد بنجاح!</string>\n    <string name=\"dialog_import_failed\">فشل الاستيراد!</string>\n    <string name=\"dialog_export_success\">تمّ التصدير بنجاح!</string>\n    <string name=\"dialog_export_failed\">فشل التصدير!</string>\n    <string name=\"refresh\">إعادة التحميل</string>\n    <string name=\"get_cookies\">الحصول على ملفات تعريف الارتباط</string>\n    <string name=\"time_settings_title_custom\">استخدام صيغة مخصصة</string>\n    <string name=\"time_settings_title_separator\">فاصل</string>\n    <string name=\"time_settings_title_time_format\">تنسيق الوقت</string>\n    <string name=\"time_settings_title_date_format\">تنسيق التاريخ</string>\n    <string name=\"time_settings_title_preview\">المعاينة</string>\n    <string name=\"time_settings_swap_time\">مبادلة اماكن الوقت والتاريخ</string>\n    <string name=\"quick_access_cannot_delete_curr\">لا يمكن حذف الحساب المستخدم حاليا</string>\n    <string name=\"quick_access_confirm_delete\">هل أنت متأكد من أنك تريد حذف \\\"%s\\\"؟</string>\n    <string name=\"open_profile\">فتح ملف شخصي</string>\n    <string name=\"view_story\">عرض القصة</string>\n    <string name=\"view_pfp\">View profile picture</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Unsupported message type</string>\n    <string name=\"dms_inbox_unsend\">Unsend message</string>\n    <string name=\"dms_inbox_giphy\">View on GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unknown media type</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media expired!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Delivered</string>\n    <string name=\"dms_inbox_raven_media_sent\">Sent</string>\n    <string name=\"dms_inbox_raven_media_opened\">Opened</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Replayed</string>\n    <string name=\"dms_inbox_raven_media_sending\">Sending…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blocked</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggested</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshotted</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Cannot deliver</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Press and hold to record audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Leave chat</string>\n    <string name=\"dms_action_leave_question\">Leave this chat?</string>\n    <string name=\"dms_action_kick\">Kick</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Fetching post(s)</string>\n    <string name=\"downloader_complete\">Download completed</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Downloading post…</string>\n    <string name=\"downloader_downloading_media\">Downloading media</string>\n    <string name=\"downloader_unknown_error\">Unknown error occurred!!!</string>\n    <string name=\"downloader_error_creating_folder\">Error creating folder!</string>\n    <string name=\"downloader_error_download_file\">Error downloading file</string>\n    <string name=\"comment_viewer_translate_comment\">Translate comment</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Followers</string>\n    <string name=\"followers_type_following\">Following</string>\n    <string name=\"followers_compare\">Comparing followers &amp; following</string>\n    <string name=\"followers_both_following\">Both following each other</string>\n    <string name=\"followers_not_following\">not following %s</string>\n    <string name=\"followers_not_follower\">%s is not following</string>\n    <string name=\"login_error_loading_cookies\">Error loading cookies</string>\n    <string name=\"comment_hint\">Write a new comment…</string>\n    <string name=\"liked_notif\">Liked your post</string>\n    <string name=\"comment_notif\">Commented on your post:</string>\n    <string name=\"follow_notif\">Started following you</string>\n    <string name=\"tagged_notif\">Tagged you in a post</string>\n    <string name=\"request_notif\">Requested following you</string>\n    <string name=\"request_approve\">Approve request</string>\n    <string name=\"request_reject\">Reject request</string>\n    <string name=\"share_public_post\">Share this public post to…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">This category is somehow empty…</string>\n    <string name=\"update_available\">An update is available! (%s)</string>\n    <string name=\"updated\">Thank you for updating Barinsta!</string>\n    <string name=\"crash_title\">App crashed</string>\n    <string name=\"crash_descr\">Oops.. the app crashed, but don\\'t worry you can send error report to the developer to help him fix the issue. (:</string>\n    <string name=\"action_notif\">Activity</string>\n    <string name=\"action_archive\">Story archive</string>\n    <string name=\"action_ayml\">Suggested users</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"zero\">You have %d notifications</item>\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"two\">You have %d notifications</item>\n        <item quantity=\"few\">You have %d notifications</item>\n        <item quantity=\"many\">You have %d notifications</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d follows</string>\n    <string name=\"activity_count_comments\">%d comments</string>\n    <string name=\"activity_count_commentlikes\">%d comment likes</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d likes</string>\n    <string name=\"activity_count_poy\">%d photos of you</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profile</string>\n    <string name=\"more\">More</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d selected</string>\n    <string name=\"logout_success\">Successfully logged out!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Mark as seen</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Start screen</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">General</string>\n    <string name=\"pref_category_theme\">Theme</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">Account</string>\n    <string name=\"account_hint\">Current login not working? Simply add the account again.</string>\n    <string name=\"add_account\">Add account</string>\n    <string name=\"about_category_license\">License (English only)</string>\n    <string name=\"about_documentation\">Visit our website</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">See our source code on GitHub</string>\n    <string name=\"about_repository_summary\">Audit, star, report bugs, contribute, and have fun (again)!</string>\n    <string name=\"about_feedback\">Send feedback by email</string>\n    <string name=\"about_category_3pt\">Third-Party Attributions</string>\n    <string name=\"reminder\">Reminder</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">White</string>\n    <string name=\"dark_black_theme\">Black</string>\n    <string name=\"light_theme_settings\">Light theme</string>\n    <string name=\"dark_theme_settings\">Dark theme</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Added to Favorites!</string>\n    <string name=\"add_to_favorites\">Add to Favorites</string>\n    <string name=\"accounts\">Accounts</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Locations</string>\n    <string name=\"unknown\">Unknown</string>\n    <string name=\"removed_from_favs\">Removed from Favourites!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Restore</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">File:</string>\n    <string name=\"enter_password\">Enter password</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">Apply</string>\n    <string name=\"save\">Save</string>\n    <string name=\"caption\">Caption</string>\n    <string name=\"edit_caption\">Edit caption</string>\n    <string name=\"translate_caption\">Translate caption</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"liking\">Liking…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">Saving…</string>\n    <string name=\"removing\">Removing…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">Downloading…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d of %2$d</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"comment\">Comment</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Share</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Show names</string>\n    <string name=\"show_avatars\">Show avatars</string>\n    <string name=\"avatar_size\">Avatar size</string>\n    <string name=\"corners\">Corners</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">Post not found!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Gallery</string>\n    <string name=\"camera\">Camera</string>\n    <string name=\"all_photos\">All Photos</string>\n    <string name=\"all_media\">All Media</string>\n    <string name=\"all_videos\">All Videos</string>\n    <string name=\"brightness\">Brightness</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exposure</string>\n    <string name=\"center\">Center</string>\n    <string name=\"color\">Color</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">End</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">إعادة تعيين</string>\n    <string name=\"crop\">قص</string>\n    <string name=\"normal\">عادية</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"zero\">%d views</item>\n        <item quantity=\"one\">%d view</item>\n        <item quantity=\"two\">%d views</item>\n        <item quantity=\"few\">%d views</item>\n        <item quantity=\"many\">%d views</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"zero\">%s stories</item>\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"two\">%s stories</item>\n        <item quantity=\"few\">%s stories</item>\n        <item quantity=\"many\">%s stories</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">التفاصيل</string>\n    <string name=\"title\">العنوان</string>\n    <string name=\"members\">الأعضاء</string>\n    <string name=\"admin\">المشرف</string>\n    <string name=\"inviter\">الداعي</string>\n    <string name=\"mute_messages\">كتم الرسائل</string>\n    <string name=\"mute_mentions\">كتم الإشارات</string>\n    <string name=\"add_members\">إضافة أعضاء</string>\n    <string name=\"search\">بحث</string>\n    <string name=\"done\">تم</string>\n    <string name=\"dms_action_make_admin\">وضع كمسؤول</string>\n    <string name=\"dms_action_remove_admin\">إزالة كمشرف</string>\n    <string name=\"edit_unsuccessful\">لم ينجح التعديل</string>\n    <string name=\"message\">الرسالة</string>\n    <string name=\"tap_to_remove\">انقر للإزالة</string>\n    <string name=\"forward\">إعادة توجيه</string>\n    <string name=\"forward_outgoing\">لقد قمت بإعادة إرسال رسالة</string>\n    <string name=\"forward_incoming\">تمت إعادة توجيه رسالة</string>\n    <string name=\"add\">إضافة</string>\n    <string name=\"send\">إرسال</string>\n    <string name=\"replying_to_yourself\">الرد على نفسك</string>\n    <string name=\"replying_to_user\">الرد على %s</string>\n    <string name=\"replied_to_yourself\">لقد قمت بالرد على نفسك</string>\n    <string name=\"replied_you\">لقد قمت بالرد</string>\n    <string name=\"replied_you_group\">لقد قمت بالرد على %s</string>\n    <string name=\"replied_group\">رد على %s</string>\n    <string name=\"replied_to_you\">رد عليك</string>\n    <string name=\"replied_to_themself\">رد على أنفسهم</string>\n    <string name=\"reacted_story_outgoing\">لقد تفاعلت مع قصتهم</string>\n    <string name=\"reacted_story_incoming\">رد فعل على قصتك</string>\n    <string name=\"mentioned_story_outgoing\">لقد ذكرتهم في قصتك</string>\n    <string name=\"mentioned_story_incoming\">ذكرك في قصتهم</string>\n    <string name=\"replied_story_outgoing\">لقد قمت بالرد على قصتهم</string>\n    <string name=\"replied_story_incoming\">رد على قصتك</string>\n    <string name=\"raven_image_expired\">انتهت صلاحية الصورة</string>\n    <string name=\"raven_image_info\">الصورة سوف تنتهي عند مشاهدتها</string>\n    <string name=\"raven_video_expired\">انتهت صلاحية الفيديو</string>\n    <string name=\"raven_video_info\">ستنتهي صلاحية الفيديو عند مشاهدته</string>\n    <string name=\"raven_msg_expired\">انتهت صلاحية الرسالة</string>\n    <string name=\"raven_msg_info\">ستنتهي صلاحية الرسالة عند مشاهدتها</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">صورة</string>\n    <string name=\"video\">فيديو</string>\n    <string name=\"voice_message\">رسالة صوتية</string>\n    <string name=\"post\">نشر</string>\n    <string name=\"approval_required_for_new_members\">الموافقة مطلوبة للانضمام</string>\n    <string name=\"requests\">الطلبات</string>\n    <string name=\"admins_only\">المشرفون فقط</string>\n    <string name=\"added_by\">تمت الإضافة بواسطة %s</string>\n    <string name=\"admin_approval_required\">موافقة المشرف مطلوبة</string>\n    <string name=\"admin_approval_required_description\">سوف تكون موافقة المشرف مطلوبة لإضافة أعضاء جدد إلى المجموعة</string>\n    <string name=\"dms_action_end\">إنهاء المحادثة</string>\n    <string name=\"dms_action_end_question\">إنهاء الدردشة؟</string>\n    <string name=\"dms_action_end_description\">سيتم إزالة جميع الأعضاء من المجموعة. سيظلون قادرين على رؤية سجل المحادثة.</string>\n    <string name=\"pending_requests\">الطلبات المعلقة</string>\n    <string name=\"accept_request_from_user\">قبول طلب من %1s (%2s)؟</string>\n    <string name=\"decline\">رفض</string>\n    <string name=\"accept\">قبول</string>\n    <string name=\"you\">أنت</string>\n    <string name=\"no_pending_requests\">لا توجد طلبات معلقة</string>\n    <string name=\"checking_for_new_messages\">التحقق من الرسائل الجديدة</string>\n    <string name=\"pref_category_stories\">قصص</string>\n    <string name=\"pref_category_dm\">رسالة مباشرة</string>\n    <string name=\"pref_category_notifications\">الإشعارات</string>\n    <string name=\"pref_category_post\">نشر</string>\n    <string name=\"enable_dm_notifications\">تمكين إشعارات DM</string>\n    <string name=\"enable_dm_auto_refesh\">تحديث تلقائي للرسائل</string>\n    <string name=\"auto_refresh_every\">تحديث تلقائي كل</string>\n    <string name=\"secs\">ثواني</string>\n    <string name=\"mins\">دقائق</string>\n    <string name=\"search_giphy\">البحث عن GIPHY</string>\n    <string name=\"generic_null_response\">الاستجابة لاغية!</string>\n    <string name=\"generic_not_ok_response\">حالة الرد ليست جيدة!</string>\n    <string name=\"generic_failed_request\">فشل الطلب!</string>\n    <string name=\"hint_keyword\">الكلمات المفتاحية</string>\n    <string name=\"toggle_keyword_filter\">تمكين تصفية الكلمات المفتاحية</string>\n    <string name=\"edit_keyword_filter\">تحرير مرشحات الكلمات المفتاحية</string>\n    <string name=\"added_keywords\">الكلمة المفتاحية: %s لقائمة التصفية</string>\n    <string name=\"removed_keywords\">تمت إزالة الكلمة المفتاحية: %s من قائمة عوامل التصفية</string>\n    <string name=\"marked_as_seen\">وضع علامة كمشاهدة</string>\n    <string name=\"delete_unsuccessful\">فشل الحذف</string>\n    <string name=\"throttle_error\">تم تاخير الاستجابة بواسطة انستاجرام بسبب الكثير من طلبات API. انتظر لبعض الوقت قبل إعادة المحاولة.</string>\n    <string name=\"error\">خطأ</string>\n    <string name=\"account_logged_out\">تم تسجيل الخروج من هذا الحساب.</string>\n    <string name=\"login_required\">تسجيل الدخول مطلوب!</string>\n    <string name=\"inactive_user\">المستخدم غير نشط!</string>\n    <string name=\"crash_report_subject\">تقرير انهيار Barinsta</string>\n    <string name=\"crash_report_title\">حدد تطبيق البريد الإلكتروني لإرسال سجلات الأعطال</string>\n    <string name=\"not_found\">غير موجود!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">تخطى هذا التحديث</string>\n    <string name=\"on_latest_version\">أنت بالفعل في الإصدار الأخير</string>\n    <string name=\"tab_order\">ترتيب الشاشة</string>\n    <string name=\"other_tabs\">التبويبات الأخرى</string>\n    <string name=\"tab_order_start_next_launch\">سيتم عرض ترتيب التبويبات عند التشغيل التالي</string>\n    <string name=\"dm_remove_warning\">إذا تم الحفظ، سيتم تعطيل جميع ميزات DM ذات الصلة في التشغيل التالي</string>\n    <string name=\"copy_caption\">نسخ التوضيحات</string>\n    <string name=\"copy_reply\">انسخ الرد</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">أعلى</string>\n    <string name=\"recent\">حديثة</string>\n    <string name=\"clear\">امسح</string>\n    <string name=\"no_external_map_app\">لم يتم العثور على تطبيق الخرائط!</string>\n    <string name=\"click_to_show_full\">انقر لعرض العدد الكلي للأعجابات</string>\n    <string name=\"no_profile_pic_found\">لم يتم العثور على صورة الملف الشخصي!</string>\n    <string name=\"swipe_up_confirmation\">هل أنت متأكد من أنك تريد فتح هذا الرابط؟</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">مشاركة عبر DM</string>\n    <string name=\"share_link\">مشاركة الرابط…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Predeterminat del sistema</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automàtic / Predeterminat del sistema</item>\n        <item>Automàtic / Seguir la bateria</item>\n        <item>Fosc</item>\n        <item>Clar</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Predeterminat d\\'Instagram (no llegits i després llegits)</item>\n        <item>De més recent a més antic</item>\n        <item>De més antic a més recent</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Ningún</item>\n        <item>\\@</item>\n        <item>a</item>\n        <item>sobre</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>segons</item>\n        <item>minuts</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Sobre</string>\n    <string name=\"action_dms\">Missatges directes</string>\n    <string name=\"action_settings\">Configuració</string>\n    <string name=\"action_download\">Descarregar</string>\n    <string name=\"action_search\">Cerca el nom d\\'usuari…</string>\n    <string name=\"action_compare\">Comparar</string>\n    <string name=\"clipboard_error\">Error copiant el text</string>\n    <string name=\"clipboard_copied\">S\\'ha copiat al porta-retalls!</string>\n    <string name=\"report\">Reportar</string>\n    <string name=\"set_password\">Protegeix el fitxer amb contrasenya</string>\n    <string name=\"password_no_max\">Contrasenya</string>\n    <string name=\"ok\">Bé</string>\n    <string name=\"yes\">Sí</string>\n    <string name=\"cancel\">Cancel·lar</string>\n    <string name=\"no\">No</string>\n    <string name=\"confirm\">Confirmar</string>\n    <string name=\"title_favorites\">Preferits</string>\n    <string name=\"title_discover\">Descobrir</string>\n    <string name=\"title_comments\">Comentaris</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Activitat</string>\n    <string name=\"update_check\">Cerca actualitzacions a l\\'inici</string>\n    <string name=\"flag_secure\">Bloca les captures de pantalla &amp; previsualització de l\\'aplicació</string>\n    <string name=\"download_user_folder\">Descarrega les publicacions a carpetes de nom d\\'usuari</string>\n    <string name=\"download_prepend_username\">Anteposa el nom d\\'usuari al nom de fitxer</string>\n    <string name=\"mark_as_seen_setting\">Marca les històries com a vistes després de visualitzar-es</string>\n    <string name=\"mark_as_seen_setting_summary\">L\\'autor de la història sabrà que l\\'has vista</string>\n    <string name=\"hide_muted_reels_setting\">Amaga les històries silenciades del canal</string>\n    <string name=\"dm_mark_as_seen_setting\">Marca els missatges com a vists després de visualitzar-los</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Altres membres sabran que l\\'has vist</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Habilitar les notificacions d\\'activitat</string>\n    <string name=\"story_sort_setting\">Ordre de les històries</string>\n    <string name=\"error_loading_profile\">S\\'ha produït un error en carregar el perfil! El nom d\\'usuari és vàlid? Si és així, és possible que se t\\'hagi restringit.</string>\n    <string name=\"error_loading_hashtag\">S\\'ha produït un error en carregar el hashtag! El nom és vàlid?</string>\n    <string name=\"error_loading_location\">S\\'ha produït un error en carregar la ubicació! L\\'URL és vàlid?</string>\n    <string name=\"error_creating_folders\">S\\'ha produït un error en crear la(es) carpeta(es) de descàrrega.</string>\n    <string name=\"select_folder\">Selecciona la carpeta</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Llengua</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPublicació</item>\n        <item quantity=\"other\">%s\\nPublicacions</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Publicació</item>\n        <item quantity=\"other\">%s Publicacions</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSeguidor</item>\n        <item quantity=\"other\">%s\\n Seguidors</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\n Seguint</string>\n    <string name=\"post_viewer_autoplay_video\">Reproduir vídeos automàticament</string>\n    <string name=\"post_viewer_background_play\">Continua reproduint els vídeos en segon pla</string>\n    <string name=\"post_viewer_background_play_summary\">No posis en pausa els vídeos quan l\\'aplicació estigui fora del focus</string>\n    <string name=\"post_viewer_muted_autoplay\">Silenciar sempre els vídeos</string>\n    <string name=\"post_viewer_show_captions\">Mostrar sempre els subtítols</string>\n    <string name=\"post_viewer_download_dialog_title\">Seleccionar el que s\\'ha de descarregar</string>\n    <string name=\"post_viewer_download_current\">Corrent</string>\n    <string name=\"post_viewer_download_album\">Àlbum complet</string>\n    <string name=\"show_stories\">Mostra les històries</string>\n    <string name=\"no_more_stories\">Prou històries!</string>\n    <string name=\"view_post\">Mostrar l\\'entrada</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Has contestat amb èxit!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d mitjana de resposta %s</item>\n        <item quantity=\"other\">%d mitjana de respostes %s</item>\n    </plurals>\n    <string name=\"slider_answer\">La teva resposta: %s</string>\n    <string name=\"reply_story\">Respon a la història</string>\n    <string name=\"reply_hint\">Resposta…</string>\n    <string name=\"story_quiz\">Qüestionari</string>\n    <string name=\"story_slider\">Desplaçador</string>\n    <string name=\"story_quizzed\">Ja has contestat!</string>\n    <string name=\"story_mentions\">Mencions</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Aquest compte és privat</string>\n    <string name=\"priv_acc_confirm\">Després de deixar de seguir, no podràs accedir a les publicacions. Estàs segur?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Pots iniciar la sessió a través de Més -&gt;; El teu compte és a l\\'extrem inferior dret o pots veure comptes públics sense iniciar una sessió!</string>\n    <string name=\"empty_acc\">Aquest compte no té publicacions</string>\n    <string name=\"empty_list\">No existeixen publicacions d\\'aquest tipus!</string>\n    <string name=\"login\">Inici de sessió</string>\n    <string name=\"logout\">Tancar la sessió</string>\n    <string name=\"logout_summary\">Navega anònimament per Instagram</string>\n    <string name=\"remove_all_acc\">Suprimeix tots els comptes</string>\n    <string name=\"remove_all_acc_warning\">Això eliminarà tots els comptes afegits de l\\'app!\\nPer eliminar només un compte, prem el compte del diàleg del commutador de comptes durant uns segons.\\nVols continuar?</string>\n    <string name=\"time_settings\">Format de data</string>\n    <string name=\"saved_create_collection\">Crear una nova col·lecció</string>\n    <string name=\"edit_collection\">Editar el nom de la col·lecció</string>\n    <string name=\"delete_collection\">Suprimeix la col·lecció</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Eliminar de la coŀlecció</string>\n    <string name=\"liked\">M\\'agrada</string>\n    <string name=\"saved\">Desat</string>\n    <string name=\"tagged\">Etiquetat</string>\n    <string name=\"dm_person\">Missatge</string>\n    <string name=\"follow\">Seguir</string>\n    <string name=\"unfollow\">Deixar de seguir</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Preferit</string>\n    <string name=\"block\">Bloquejar</string>\n    <string name=\"unblock\">Desbloquejar</string>\n    <string name=\"restrict\">Restringir</string>\n    <string name=\"unrestrict\">Deixar de restringir</string>\n    <string name=\"mute_stories\">Silenciar històries</string>\n    <string name=\"mute_posts\">Silenciar publicacions</string>\n    <string name=\"unmute_stories\">No silenciar les històries</string>\n    <string name=\"unmute_posts\">No silenciar les publicacions</string>\n    <string name=\"remove_follower\">Elimina el seguidor</string>\n    <string name=\"bio_copy\">Copiar bio</string>\n    <string name=\"bio_translate\">Traduir bio</string>\n    <string name=\"status_mutual\">Mutu</string>\n    <string name=\"status_following\">Seguint</string>\n    <string name=\"status_follower\">Seguidor</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Comptes</string>\n    <string name=\"dialog_export_settings\">Configuració</string>\n    <string name=\"dialog_export_favorites\">Preferits</string>\n    <string name=\"dialog_import_success\">S\\'ha importat correctament!</string>\n    <string name=\"dialog_import_failed\">S\\'ha produït un error a l\\'hora d\\'importar!</string>\n    <string name=\"dialog_export_success\">S\\'ha exportat correctament!</string>\n    <string name=\"dialog_export_failed\">No s\\'ha pogut exportar!</string>\n    <string name=\"refresh\">Actualitzar</string>\n    <string name=\"get_cookies\">Obtenir cookies</string>\n    <string name=\"time_settings_title_custom\">Utilitza un format personalitzat</string>\n    <string name=\"time_settings_title_separator\">Separador</string>\n    <string name=\"time_settings_title_time_format\">Format d\\'hora</string>\n    <string name=\"time_settings_title_date_format\">Format de data</string>\n    <string name=\"time_settings_title_preview\">Previsualitzar</string>\n    <string name=\"time_settings_swap_time\">Intercanvia les posicions d\\'hora i data</string>\n    <string name=\"quick_access_cannot_delete_curr\">No es pot eliminar el compte actualment en ús</string>\n    <string name=\"quick_access_confirm_delete\">Esteu segur que vols suprimir \\\"%s\\\"?</string>\n    <string name=\"open_profile\">Obre el perfil</string>\n    <string name=\"view_story\">Veure història</string>\n    <string name=\"view_pfp\">Mostra la imatge del perfil</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Tipus de missatge no acceptat</string>\n    <string name=\"dms_inbox_unsend\">Anul·lar l\\'enviament del missatge</string>\n    <string name=\"dms_inbox_giphy\">Veure a GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s ha compartit una publicació de @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s ha compartit una imatge</string>\n    <string name=\"dms_inbox_shared_video\">%s ha compartit un vídeo</string>\n    <string name=\"dms_inbox_shared_message\">%s ha enviat un missatge</string>\n    <string name=\"dms_inbox_shared_gif\">%s ha compartit un gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s ha compartit un adhesiu</string>\n    <string name=\"dms_inbox_shared_profile\">%s ha compartit un perfil: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s ha compartit una ubicació: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s ha compartit una història destacada de @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s ha compartit una història de @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s ha enviat un missatge de veu</string>\n    <string name=\"dms_inbox_shared_clip\">%s ha compartit un clip de @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s ha compartit un video IGTV de @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Has respost a la seva història: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s ha contestat a la teva història: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Has reaccionat a la seva història: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s ha reaccionat a la teva història: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Has mencionat @%s en la teva història</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s t\\'ha mencionat a la seva història</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Tipus de contingut desconegut</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">L\\'arxiu multimèdia ha caducat!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Lliurat</string>\n    <string name=\"dms_inbox_raven_media_sent\">Enviat</string>\n    <string name=\"dms_inbox_raven_media_opened\">Obert</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Repetit</string>\n    <string name=\"dms_inbox_raven_media_sending\">Enviant…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Bloquejat</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggerit</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Captura de pantalla feta</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">No es pot lliurar</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Premeu i mantingueu premut per enregistrar l\\'àudio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Deixar el xat</string>\n    <string name=\"dms_action_leave_question\">Vols sortir d\\'aquest xat?</string>\n    <string name=\"dms_action_kick\">Expulsar</string>\n    <string name=\"dms_left_users\">Usuaris que han marxat</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Usuari/a invàlid</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram no permet pujar vídeos més de 60 segons per DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram no permet pujar àudio de més de 60 segons.</string>\n    <string name=\"direct_download_loading\">S\\'estan recuperant les noves publicacions</string>\n    <string name=\"downloader_complete\">S\\'ha completat la descàrrega</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Descarregant publicació…</string>\n    <string name=\"downloader_downloading_media\">Descarregant multimèdia</string>\n    <string name=\"downloader_unknown_error\">S\\'ha produït un error desconegut!!!</string>\n    <string name=\"downloader_error_creating_folder\">S\\'ha produït un error en crear la carpeta!</string>\n    <string name=\"downloader_error_download_file\">S\\'ha produït un error en descarregar l\\'arxiu</string>\n    <string name=\"comment_viewer_translate_comment\">Traduir comentari</string>\n    <string name=\"comment_viewer_delete_comment\">Esborrar comentari</string>\n    <string name=\"followers_type_followers\">Seguidors</string>\n    <string name=\"followers_type_following\">Seguits</string>\n    <string name=\"followers_compare\">Comparant seguidors &amp; seguint</string>\n    <string name=\"followers_both_following\">Ambdós es segueixen mútuament</string>\n    <string name=\"followers_not_following\">no segueix %s</string>\n    <string name=\"followers_not_follower\">%s no està seguint</string>\n    <string name=\"login_error_loading_cookies\">Error carregant els cookies</string>\n    <string name=\"comment_hint\">Escriure un comentari nou…</string>\n    <string name=\"liked_notif\">Li ha agradat la teva publicació</string>\n    <string name=\"comment_notif\">Ha comentat en la teva publciació:</string>\n    <string name=\"follow_notif\">Ha començat a seguir-te</string>\n    <string name=\"tagged_notif\">T\\'ha etiquetat en una publicació</string>\n    <string name=\"request_notif\">Ha sol·licitat seguir-te</string>\n    <string name=\"request_approve\">Aprovar la sol·licitud</string>\n    <string name=\"request_reject\">Rebutjar la sol·licitud</string>\n    <string name=\"share_public_post\">Comparteix aquesta publicació pública a…</string>\n    <string name=\"share_private_post\">Aquesta és una publicació privada! Comparteix-la amb aquells que la puguin veure.</string>\n    <string name=\"discover_empty\">Aquesta categoria està buida d\\'alguna manera…</string>\n    <string name=\"update_available\">Hi ha disponible una actualització! (%s)</string>\n    <string name=\"updated\">Gràcies per actualitzar Barinsta!</string>\n    <string name=\"crash_title\">L\\'aplicació ha fallat</string>\n    <string name=\"crash_descr\">Vaja... L\\'aplicació ha fallat, però no et preocupis, pots enviar un informe d\\'error al desenvolupador per ajudar-lo a solucionar el problema. (:</string>\n    <string name=\"action_notif\">Activitat</string>\n    <string name=\"action_archive\">Arxiu de històries</string>\n    <string name=\"action_ayml\">Usuaris suggerits</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d seguidors</string>\n    <string name=\"activity_count_comments\">%d comentaris</string>\n    <string name=\"activity_count_commentlikes\">%d m\\'agrades al comentari</string>\n    <string name=\"activity_count_usertags\">%d tags d\\'usuari</string>\n    <string name=\"activity_count_likes\">%d m\\'agrades</string>\n    <string name=\"activity_count_poy\">%d fotos de tu</string>\n    <string name=\"activity_count_requests\">%d sol·licituds de seguiment</string>\n    <string name=\"activity_notloggedin\">Has tancat la sessió abans de fer clic a aquesta notificació?!</string>\n    <string name=\"feed\">Fil</string>\n    <string name=\"profile\">Perfil</string>\n    <string name=\"more\">Més</string>\n    <string name=\"title_dm\">MD</string>\n    <string name=\"number_selected\">%d seleccionat(s)</string>\n    <string name=\"logout_success\">S\\'ha tancat la sessió correctament!</string>\n    <string name=\"dm_thread_info\">Informació</string>\n    <string name=\"mark_as_seen\">Marcar com a llegit</string>\n    <string name=\"version\">Versió</string>\n    <string name=\"pref_start_screen\">Pantalla d\\'inici</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Mostra el teclat al cercar</string>\n    <string name=\"pref_category_general\">General</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Descàrregues</string>\n    <string name=\"pref_category_locale\">Local</string>\n    <string name=\"account\">Compte</string>\n    <string name=\"account_hint\">L\\'inici de sessió actual no funciona? Simplement afegeix el compte de nou.</string>\n    <string name=\"add_account\">Afegir compte</string>\n    <string name=\"about_category_license\">Llicència (només en anglès)</string>\n    <string name=\"about_documentation\">Visita la nostra pàgina web</string>\n    <string name=\"about_documentation_summary\">Obtén suport, parla, trobat amb altres i diverteix-te!</string>\n    <string name=\"about_repository\">Veure el nostre codi font a GitHub</string>\n    <string name=\"about_repository_summary\">Inspecta, segueix, informa d\\'errors, contribueix i diverteix-te (una altra vegada)!</string>\n    <string name=\"about_feedback\">Enviar comentaris per correu electrònic</string>\n    <string name=\"about_category_3pt\">Reconeixements de tercers</string>\n    <string name=\"reminder\">Recordatori</string>\n    <string name=\"reminder_summary\">Utilitza aquesta aplicació de manera responsable. Les imatges baixades només s\\'han d\\'utilitzar per a propòsits permesos per les lleis aplicables.</string>\n    <string name=\"light_white_theme\">Blanc</string>\n    <string name=\"dark_black_theme\">Negre</string>\n    <string name=\"light_theme_settings\">Tema clar</string>\n    <string name=\"dark_theme_settings\">Tema fosc</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">S\\'ha afegit als preferits!</string>\n    <string name=\"add_to_favorites\">Afegir a favorits</string>\n    <string name=\"accounts\">Comptes</string>\n    <string name=\"hashtags\">Etiquetes</string>\n    <string name=\"locations\">Ubicacions</string>\n    <string name=\"unknown\">Desconegut</string>\n    <string name=\"removed_from_favs\">Eliminat dels preferits!</string>\n    <string name=\"backup_and_restore\">Fer còpia de seguretat &amp; restaurar</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Fer una còpia de seguretat de la configuració de l\\'aplicació Barinsta, la informació d\\'inici de sessió del compte, i/o dades preferides a un text net o arxiu de còpia de seguretat xifrat per a la restauració posterior.</string>\n    <string name=\"backup_warning\">Si estàs fent una còpia de seguretat de les dades d\\'inici de sessió del compte, considera el fitxer com a confidencial i mentén-lo segur en algun lloc!</string>\n    <string name=\"create_backup\">Crear un nou fitxer de còpia de seguretat</string>\n    <string name=\"restore_backup\">Restaurar des d\\'un fitxer de còpia de seguretat existent</string>\n    <string name=\"file_chosen_label\">Arxiu:</string>\n    <string name=\"enter_password\">Introduir la contrasenya</string>\n    <string name=\"select_backup_file\">Selecciona un fitxer de còpia de seguretat (.zaai/.backup)</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"save\">Desar</string>\n    <string name=\"caption\">Llegenda</string>\n    <string name=\"edit_caption\">Editar llegenda</string>\n    <string name=\"translate_caption\">Traduir la llegenda</string>\n    <string name=\"player_timeline_desc\">Línia de temps del reproductor de vídeo</string>\n    <string name=\"liking\">Donant m\\'agrada…</string>\n    <string name=\"like_unsuccessful\">El \\\"m\\'agrada\\\" ha fallat</string>\n    <string name=\"unlike_unsuccessful\">Ha fallat treure el \\\"m\\'agrada\\\"</string>\n    <string name=\"unliking\">Desfent \\\"m\\'agrada\\\"…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">Desant…</string>\n    <string name=\"removing\">Eliminant…</string>\n    <string name=\"save_unsuccessful\">No s\\'ha pogut desar</string>\n    <string name=\"save_remove_unsuccessful\">No s\\'ha pogut suprimir</string>\n    <string name=\"downloading\">Descarregant…</string>\n    <string name=\"downloader_downloading_child\">Descarregat item %1$d de %2$d</string>\n    <string name=\"delete\">Eliminar</string>\n    <string name=\"comment\">Comentar</string>\n    <string name=\"layout\">Disposició</string>\n    <string name=\"feed_stories\">Fil d\\'històries</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Compartir</string>\n    <string name=\"layout_style\">Estil de disposició</string>\n    <string name=\"column_count\">Nombre de columnes</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Mostrar els noms</string>\n    <string name=\"show_avatars\">Mostrar avatars</string>\n    <string name=\"avatar_size\">Talla de l\\'avatar</string>\n    <string name=\"corners\">Cantonades</string>\n    <string name=\"show_grid_gap\">Mostrar la separació de la graella</string>\n    <string name=\"post_not_found\">Publicació no trobada!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galeria</string>\n    <string name=\"camera\">Càmera</string>\n    <string name=\"all_photos\">Totes les fotografies</string>\n    <string name=\"all_media\">Tot el contingut multimèdia</string>\n    <string name=\"all_videos\">Tots els vídeos</string>\n    <string name=\"brightness\">Brillantor</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturació</string>\n    <string name=\"sharpen\">Aguditza</string>\n    <string name=\"exposure\">Exposició</string>\n    <string name=\"center\">Centrar</string>\n    <string name=\"color\">Color</string>\n    <string name=\"start\">Inici</string>\n    <string name=\"end\">Fi</string>\n    <string name=\"bilateral_blur\">Blurt bilateral</string>\n    <string name=\"vignette\">Vinyeta</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sèpia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Restablir</string>\n    <string name=\"crop\">Escapçar</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d vista</item>\n        <item quantity=\"other\">%d vistes</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s història</item>\n        <item quantity=\"other\">%s històries</item>\n    </plurals>\n    <string name=\"details\">Detalls</string>\n    <string name=\"title\">Títol</string>\n    <string name=\"members\">Membres</string>\n    <string name=\"admin\">Administrador</string>\n    <string name=\"inviter\">Convidador</string>\n    <string name=\"mute_messages\">Silencia els missatges</string>\n    <string name=\"mute_mentions\">Silencia les mencions</string>\n    <string name=\"add_members\">Afegir membres</string>\n    <string name=\"search\">Cercar</string>\n    <string name=\"done\">Fet</string>\n    <string name=\"dms_action_make_admin\">Fer administrador</string>\n    <string name=\"dms_action_remove_admin\">Elimina com a administrador</string>\n    <string name=\"edit_unsuccessful\">L\\'edició no ha tingut èxit</string>\n    <string name=\"message\">Missatge</string>\n    <string name=\"tap_to_remove\">Toca per eliminar</string>\n    <string name=\"forward\">Reenviar</string>\n    <string name=\"forward_outgoing\">Has reenviat un missatge</string>\n    <string name=\"forward_incoming\">S\\'ha reenviat un missatge</string>\n    <string name=\"add\">Afegir</string>\n    <string name=\"send\">Enviar</string>\n    <string name=\"replying_to_yourself\">Responent-te a tu mateix</string>\n    <string name=\"replying_to_user\">Responent a %s</string>\n    <string name=\"replied_to_yourself\">Has contestat a tu mateix</string>\n    <string name=\"replied_you\">Has contestat</string>\n    <string name=\"replied_you_group\">Has respost a %s</string>\n    <string name=\"replied_group\">Respost a %s</string>\n    <string name=\"replied_to_you\">Respost a tu</string>\n    <string name=\"replied_to_themself\">S\\'han respost a ells mateixos</string>\n    <string name=\"reacted_story_outgoing\">Has reaccionat a la seva història</string>\n    <string name=\"reacted_story_incoming\">Ha reaccionat a la teva història</string>\n    <string name=\"mentioned_story_outgoing\">Els ha esmentat en la seva història</string>\n    <string name=\"mentioned_story_incoming\">T\\'ha mencionat a la seva història</string>\n    <string name=\"replied_story_outgoing\">Has respost a la seva història</string>\n    <string name=\"replied_story_incoming\">Ha contestat a la teva història</string>\n    <string name=\"raven_image_expired\">La imatge ha caducat</string>\n    <string name=\"raven_image_info\">La imatge caducarà quan es vegi</string>\n    <string name=\"raven_video_expired\">El vídeo ha caducat</string>\n    <string name=\"raven_video_info\">El vídeo caducarà quan es vegi</string>\n    <string name=\"raven_msg_expired\">El missatge ha caducat</string>\n    <string name=\"raven_msg_info\">El missatge caducarà quan es vegi</string>\n    <string name=\"story_share\">Història de @%s</string>\n    <string name=\"story_share_highlight\">Història destacada de @%s</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Vídeo</string>\n    <string name=\"voice_message\">Missatge de veu</string>\n    <string name=\"post\">Publicació</string>\n    <string name=\"approval_required_for_new_members\">Es requereix aprovació per unir-se</string>\n    <string name=\"requests\">Sol·licituds</string>\n    <string name=\"admins_only\">Només administradors</string>\n    <string name=\"added_by\">Afegit per %s</string>\n    <string name=\"admin_approval_required\">Cal l\\'aprovació de l\\'administrador</string>\n    <string name=\"admin_approval_required_description\">Cal una aprovació d\\'administrador per afegir nous membres al grup</string>\n    <string name=\"dms_action_end\">Finalitzar el xat</string>\n    <string name=\"dms_action_end_question\">Finalitzar el xat?</string>\n    <string name=\"dms_action_end_description\">Tots els membres seran retirats del grup. Encara podran veure l\\'historial de xat.</string>\n    <string name=\"pending_requests\">Sol·licituds pendents</string>\n    <string name=\"accept_request_from_user\">Voleu acceptar la sol·licitud de %1s (%2s)?</string>\n    <string name=\"decline\">Rebutjar</string>\n    <string name=\"accept\">Acceptar</string>\n    <string name=\"you\">Tu</string>\n    <string name=\"no_pending_requests\">Cap sol·licitud pendent</string>\n    <string name=\"checking_for_new_messages\">S\\'estan comprovant els missatges nous</string>\n    <string name=\"pref_category_stories\">Històries</string>\n    <string name=\"pref_category_dm\">MP</string>\n    <string name=\"pref_category_notifications\">Notificacions</string>\n    <string name=\"pref_category_post\">Publicació</string>\n    <string name=\"enable_dm_notifications\">Habilita les notificacions pels missatges privats (MP)</string>\n    <string name=\"enable_dm_auto_refesh\">Actualitza automàticament els missatges</string>\n    <string name=\"auto_refresh_every\">Actualitza automàticament cada</string>\n    <string name=\"secs\">segons</string>\n    <string name=\"mins\">minuts</string>\n    <string name=\"search_giphy\">Cercar GIPHY</string>\n    <string name=\"generic_null_response\">La resposta és nul·la!</string>\n    <string name=\"generic_not_ok_response\">L\\'estat de la resposta no és correcte!</string>\n    <string name=\"generic_failed_request\">La petició ha fallat!</string>\n    <string name=\"hint_keyword\">Paraula clau</string>\n    <string name=\"toggle_keyword_filter\">Activar el filtre de paraules clau</string>\n    <string name=\"edit_keyword_filter\">Editar els filtres de paraules clau</string>\n    <string name=\"added_keywords\">S\\'ha afegit la paraula clau: %s a la llista de filtres</string>\n    <string name=\"removed_keywords\">S\\'ha eliminat la paraula clau: %s de la llista de filtres</string>\n    <string name=\"marked_as_seen\">Marcat com a vist</string>\n    <string name=\"delete_unsuccessful\">Suprimeix sense èxit</string>\n    <string name=\"throttle_error\">Restringit per Instagram a causa de massa sol·licituds a l\\'API. Espera un temps abans de tornar a intentar-ho.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">S\\'ha desconnectat aquest compte.</string>\n    <string name=\"login_required\">Es requereix iniciar la sessió!</string>\n    <string name=\"inactive_user\">L\\'usuari està inactiu!</string>\n    <string name=\"crash_report_subject\">Informe d\\'error de Barinsta</string>\n    <string name=\"crash_report_title\">Selecciona una aplicació de correu electrònic per enviar registres d\\'error</string>\n    <string name=\"not_found\">No s\\'ha trobat!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Omet aquesta actualització</string>\n    <string name=\"on_latest_version\">Ja esteu a la darrera versió</string>\n    <string name=\"tab_order\">Ordre de la pantalla</string>\n    <string name=\"other_tabs\">Altres pestanyes</string>\n    <string name=\"tab_order_start_next_launch\">L\\'ordre de les pestanyes es reflectirà en el següent llançament</string>\n    <string name=\"dm_remove_warning\">Si es desa, totes les característiques relacionades amb els MD es desactivaran en el següent llançament</string>\n    <string name=\"copy_caption\">Copia la llegenda</string>\n    <string name=\"copy_reply\">Copia la resposta</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Part superior</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Netejar</string>\n    <string name=\"no_external_map_app\">No s\\'ha trobat cap aplicació de navegació!</string>\n    <string name=\"click_to_show_full\">Feu clic per mostrar el recompte complet de m\\'agrada</string>\n    <string name=\"no_profile_pic_found\">No s\\'ha trobat cap imatge de perfil!</string>\n    <string name=\"swipe_up_confirmation\">Esteu segur que voleu obrir aquest enllaç?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Comparteix via MD</string>\n    <string name=\"share_link\">Comparteix l\\'enllaç...</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Podle systému</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automaticky / Podle systému</item>\n        <item>Automaticky / Podle baterie</item>\n        <item>Tmavý</item>\n        <item>Světlý</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Výchozí chování Instagramu (nepřečtené pak přečtené)</item>\n        <item>Od nejnovějších po nejstarší</item>\n        <item>Od nejstarších po nejnovější</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Žádný</item>\n        <item>\\@</item>\n        <item>v</item>\n        <item>ze dne</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>sekundy</item>\n        <item>minuty</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">O aplikaci</string>\n    <string name=\"action_dms\">Přímé Zprávy</string>\n    <string name=\"action_settings\">Nastavení</string>\n    <string name=\"action_download\">Stáhnout</string>\n    <string name=\"action_search\">Hledat uživatele…</string>\n    <string name=\"action_compare\">Porovnat</string>\n    <string name=\"clipboard_error\">Chyba při kopírování textu</string>\n    <string name=\"clipboard_copied\">Zkopírováno do schránky!</string>\n    <string name=\"report\">Nahlásit</string>\n    <string name=\"set_password\">Chránit soubor heslem</string>\n    <string name=\"password_no_max\">Heslo</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Ano</string>\n    <string name=\"cancel\">Zrušit</string>\n    <string name=\"no\">Ne</string>\n    <string name=\"confirm\">Potvrdit</string>\n    <string name=\"title_favorites\">Oblíbené</string>\n    <string name=\"title_discover\">Objevit</string>\n    <string name=\"title_comments\">Komentáře</string>\n    <string name=\"title_replies\">Odpovědi</string>\n    <string name=\"title_notifications\">Aktivita</string>\n    <string name=\"update_check\">Zkontrolovat aktualizace při spuštění</string>\n    <string name=\"flag_secure\">Blokovat snímky obrazovky a náhled aplikace</string>\n    <string name=\"download_user_folder\">Stáhnout příspěvky do složek se jmény uživatelů</string>\n    <string name=\"download_prepend_username\">Připojit jméno uživatele k názvu souboru</string>\n    <string name=\"mark_as_seen_setting\">Po zobrazení označit příběhy jako zobrazené</string>\n    <string name=\"mark_as_seen_setting_summary\">Autor příběhu bude vědět, že jste si jej zobrazili</string>\n    <string name=\"hide_muted_reels_setting\">Skrýt ztišené příběhy z kanálu</string>\n    <string name=\"dm_mark_as_seen_setting\">Označovat zprávy po zobrazení jako zobrazené</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Ostatní členové uvidí, že jste si ji je zobrazili</string>\n    <string name=\"autoplay_stories_setting\">Automaticky přehrát video příběhy</string>\n    <string name=\"story_list_setting\">Zobrazovat seznam příběhů automaticky</string>\n    <string name=\"story_list_setting_summary\">Při prohlížení příběhů</string>\n    <string name=\"activity_setting\">Povolit oznámení o aktivitě</string>\n    <string name=\"story_sort_setting\">Řazení kanálu příběhů</string>\n    <string name=\"error_loading_profile\">Chyba při načítání profilu! Je uživatelské jméno platné? Pokud ano, jste možná omezeni Instagramem.</string>\n    <string name=\"error_loading_hashtag\">Chyba při načítání hashtagu! Je název platný?</string>\n    <string name=\"error_loading_location\">Chyba při načítání polohy! Je URL platná?</string>\n    <string name=\"error_creating_folders\">Chyba při vytváření složky(ek) stahování.</string>\n    <string name=\"select_folder\">Vybrat složku</string>\n    <string name=\"theme_settings\">Motiv</string>\n    <string name=\"select_language\">Jazyk</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPříspěvek</item>\n        <item quantity=\"few\">%s\\nPříspěvky</item>\n        <item quantity=\"many\">%s\\nPříspěvků</item>\n        <item quantity=\"other\">%s\\nPříspěvky</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s příspěvek</item>\n        <item quantity=\"few\">%s příspěvky</item>\n        <item quantity=\"many\">%s příspěvků</item>\n        <item quantity=\"other\">%s příspěvky</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSledující</item>\n        <item quantity=\"few\">%s\\nSledující</item>\n        <item quantity=\"many\">%s\\nSledujících</item>\n        <item quantity=\"other\">%s\\nSledujících</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nSleduje</string>\n    <string name=\"post_viewer_autoplay_video\">Automaticky přehrávat videa</string>\n    <string name=\"post_viewer_background_play\">Pokračovat v přehrávání na pozadí</string>\n    <string name=\"post_viewer_background_play_summary\">Nepozastavovat videa při opuštění aplikace</string>\n    <string name=\"post_viewer_muted_autoplay\">Vždy ztlumit videa</string>\n    <string name=\"post_viewer_show_captions\">Vždy zobrazovat popisek příspěvku</string>\n    <string name=\"post_viewer_download_dialog_title\">Vyberte, co stáhnout</string>\n    <string name=\"post_viewer_download_current\">Aktuální</string>\n    <string name=\"post_viewer_download_album\">Celé album</string>\n    <string name=\"show_stories\">Zobrazit příběhy</string>\n    <string name=\"no_more_stories\">Žádné další příběhy!</string>\n    <string name=\"view_post\">Zobrazit příspěvek</string>\n    <string name=\"story_poll\">Hlasování</string>\n    <string name=\"answered_story\">Odpověď úspěšně odeslána!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d odpověď, průměrně %s</item>\n        <item quantity=\"few\">%d odpovědi, průměrně %s</item>\n        <item quantity=\"many\">%d odpovědí, průměrně %s</item>\n        <item quantity=\"other\">%d odpovědí, průměrně %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Vaše odpověď: %s</string>\n    <string name=\"reply_story\">Odpovědět na příběh</string>\n    <string name=\"reply_hint\">Odpovědět…</string>\n    <string name=\"story_quiz\">Kvíz</string>\n    <string name=\"story_slider\">Posuvník</string>\n    <string name=\"story_quizzed\">Už jste odpověděli!</string>\n    <string name=\"story_mentions\">Zmínky</string>\n    <string name=\"story_question\">Otázka</string>\n    <string name=\"priv_acc\">Tento účet je soukromý</string>\n    <string name=\"priv_acc_confirm\">Po zrušení sledování nebudete mít přístup k příspěvkům! Jste si jisti?</string>\n    <string name=\"are_you_sure\">Určitě to chcete udělat?</string>\n    <string name=\"no_acc\">Můžete se přihlásit přes Více -&gt; Účet v pravém dolním rohu nebo můžete zobrazit veřejné účty bez přihlášení!</string>\n    <string name=\"empty_acc\">Tento účet nezveřejnil žádné příspěvky</string>\n    <string name=\"empty_list\">Žádné takové příspěvky!</string>\n    <string name=\"login\">Přihlásit se</string>\n    <string name=\"logout\">Odhlásit se</string>\n    <string name=\"logout_summary\">Procházet Instagram anonymně</string>\n    <string name=\"remove_all_acc\">Odstranit všechny účty</string>\n    <string name=\"remove_all_acc_warning\">Tato akce z aplikace odstraní všechny přidané účty!\\nChcete-li odstranit jen jeden účet, dlouze ho podržte v přepínači účtů.\\nChcete stále pokračovat?</string>\n    <string name=\"time_settings\">Formát data</string>\n    <string name=\"saved_create_collection\">Vytvořit sbírku</string>\n    <string name=\"edit_collection\">Upravit název sbírky</string>\n    <string name=\"delete_collection\">Odstranit sbírku</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Přidat do sbírky…</string>\n    <string name=\"remove_from_collection\">Odebrat ze sbírky</string>\n    <string name=\"liked\">Líbí se</string>\n    <string name=\"saved\">Uložené</string>\n    <string name=\"tagged\">Označení</string>\n    <string name=\"dm_person\">Zpráva</string>\n    <string name=\"follow\">Sledovat</string>\n    <string name=\"unfollow\">Přestat sledovat</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Oblíbené</string>\n    <string name=\"block\">Zablokovat</string>\n    <string name=\"unblock\">Odblokovat</string>\n    <string name=\"restrict\">Omezit</string>\n    <string name=\"unrestrict\">Zrušit omezení</string>\n    <string name=\"mute_stories\">Ztlumit příběhy</string>\n    <string name=\"mute_posts\">Ztlumit příspěvky</string>\n    <string name=\"unmute_stories\">Zrušit ztlumení příběhů</string>\n    <string name=\"unmute_posts\">Zrušit ztlumení příspěvků</string>\n    <string name=\"remove_follower\">Odebrat sledujícího</string>\n    <string name=\"bio_copy\">Kopírovat bio</string>\n    <string name=\"bio_translate\">Přeložit bio</string>\n    <string name=\"status_mutual\">Vzájemní</string>\n    <string name=\"status_following\">Sleduje</string>\n    <string name=\"status_follower\">Sledující</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Účty</string>\n    <string name=\"dialog_export_settings\">Nastavení</string>\n    <string name=\"dialog_export_favorites\">Oblíbené</string>\n    <string name=\"dialog_import_success\">Úspěšně importováno!</string>\n    <string name=\"dialog_import_failed\">Import se nezdařil!</string>\n    <string name=\"dialog_export_success\">Úspěšně exportováno!</string>\n    <string name=\"dialog_export_failed\">Export se nezdařil!</string>\n    <string name=\"refresh\">Načíst znovu</string>\n    <string name=\"get_cookies\">Získat cookies</string>\n    <string name=\"time_settings_title_custom\">Použít vlastní formát</string>\n    <string name=\"time_settings_title_separator\">Oddělovač</string>\n    <string name=\"time_settings_title_time_format\">Formát času</string>\n    <string name=\"time_settings_title_date_format\">Formát data</string>\n    <string name=\"time_settings_title_preview\">Náhled</string>\n    <string name=\"time_settings_swap_time\">Prohodit pozice času a data</string>\n    <string name=\"quick_access_cannot_delete_curr\">Nemůžete smazat aktuálně používaný účet</string>\n    <string name=\"quick_access_confirm_delete\">Opravdu chcete smazat \\\"%s\\\"?</string>\n    <string name=\"open_profile\">Otevřít profil</string>\n    <string name=\"view_story\">Zobrazit příběh</string>\n    <string name=\"view_pfp\">Zobrazit obrázek profilu</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Nepodporovaný typ zprávy</string>\n    <string name=\"dms_inbox_unsend\">Zrušit odeslání zprávy</string>\n    <string name=\"dms_inbox_giphy\">Zobrazit na GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s sdílel(a) příspěvek od @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s sdílel(a) obrázek</string>\n    <string name=\"dms_inbox_shared_video\">%s sdílel(a) video</string>\n    <string name=\"dms_inbox_shared_message\">%s poslal(a) zprávu</string>\n    <string name=\"dms_inbox_shared_gif\">%s sdílel(a) gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s sdílel(a) nálepku</string>\n    <string name=\"dms_inbox_shared_profile\">%s sdílel(a) profil: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s sdílel(a) polohu: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s sdílel(a) zvýrazněný příběh od @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s sdílel(a) příběh od @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s poslal(a) hlasovou zprávu</string>\n    <string name=\"dms_inbox_shared_clip\">%s sdílel(a) sekvenci od @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s sdílel(a) IGTV video od @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Odpověděli jste na příběh: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s odpověděl(a) na váš příběh: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Reagovali jste na příběh: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reagoval(a) na váš příběh: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Zmínili jste @%s ve svém příběhu</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s vás zmínil(a) ve svém příběhu</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Neznámý typ médií</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Platnost média vypršela!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Doručeno</string>\n    <string name=\"dms_inbox_raven_media_sent\">Odesláno</string>\n    <string name=\"dms_inbox_raven_media_opened\">Otevřeno</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Znovu přehráno</string>\n    <string name=\"dms_inbox_raven_media_sending\">Odesílám…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blokován</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Navrhované</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Udělán snímek obrazovky</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Nelze doručit</string>\n    <string name=\"dms_thread_message_hint\">Zpráva…</string>\n    <string name=\"dms_thread_audio_hint\">Stiskněte a podržte pro nahrávání</string>\n    <string name=\"dms_thread_updating\">Aktualizace…</string>\n    <string name=\"dms_action_leave\">Opustit chat</string>\n    <string name=\"dms_action_leave_question\">Chcete opustit tento chat?</string>\n    <string name=\"dms_action_kick\">Vyhodit</string>\n    <string name=\"dms_left_users\">Uživatelé, kteří skupinu opustili</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Neexistující uživatel</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram neumožňuje posílat ve zprávách videa delší než 60 sekund.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram neumožňuje posílat ve zprávách zvuk delší než 60 sekund.</string>\n    <string name=\"direct_download_loading\">Načítání příspěvků</string>\n    <string name=\"downloader_complete\">Stahování bylo dokončeno</string>\n    <string name=\"downloader_preparing\">Připravuje se stahování…</string>\n    <string name=\"downloader_downloading_post\">Stahování příspěvku…</string>\n    <string name=\"downloader_downloading_media\">Stahování médií</string>\n    <string name=\"downloader_unknown_error\">Vyskytla se neznámá chyba!</string>\n    <string name=\"downloader_error_creating_folder\">Chyba při vytváření složky!</string>\n    <string name=\"downloader_error_download_file\">Chyba při stahování souboru</string>\n    <string name=\"comment_viewer_translate_comment\">Přeložit komentář</string>\n    <string name=\"comment_viewer_delete_comment\">Odstranit komentář</string>\n    <string name=\"followers_type_followers\">Sledující</string>\n    <string name=\"followers_type_following\">Sleduje</string>\n    <string name=\"followers_compare\">Porovnání sledujících a sledovaných</string>\n    <string name=\"followers_both_following\">Oba(ě) se navzájem sledují</string>\n    <string name=\"followers_not_following\">nesleduje %s</string>\n    <string name=\"followers_not_follower\">%s nesleduje</string>\n    <string name=\"login_error_loading_cookies\">Chyba při načítání souborů cookies</string>\n    <string name=\"comment_hint\">Napsat nový komentář…</string>\n    <string name=\"liked_notif\">Váš příspěvek se líbí</string>\n    <string name=\"comment_notif\">Okomentoval(a) váš příspěvek:</string>\n    <string name=\"follow_notif\">Vás začal(a) sledovat</string>\n    <string name=\"tagged_notif\">Vás označil(a) v příspěvku</string>\n    <string name=\"request_notif\">Požádal(a) o sledování</string>\n    <string name=\"request_approve\">Schválit žádost</string>\n    <string name=\"request_reject\">Zamítnout žádost</string>\n    <string name=\"share_public_post\">Sdílejte tento veřejný příspěvek na…</string>\n    <string name=\"share_private_post\">Toto je soukromý příspěvek! Sdílejte jej s těmi, kdo jej mohou vidět.</string>\n    <string name=\"discover_empty\">Tato kategorie je nějak prázdná…</string>\n    <string name=\"update_available\">Je k dispozici aktualizace! (%s)</string>\n    <string name=\"updated\">Děkujeme, že jste aktualizovali Barinstu!</string>\n    <string name=\"crash_title\">Aplikace spadla</string>\n    <string name=\"crash_descr\">Oops.. aplikace se zhroutila, ale nebojte se, můžete poslat chybové hlášení vývojáři, abyste mu pomohli vyřešit tuto chybu. (:</string>\n    <string name=\"action_notif\">Aktivita</string>\n    <string name=\"action_archive\">Archiv příběhů</string>\n    <string name=\"action_ayml\">Navrhovaní uživatelé</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"few\">You have %d notifications</item>\n        <item quantity=\"many\">You have %d notifications</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d sleduje</string>\n    <string name=\"activity_count_comments\">%d komentářů</string>\n    <string name=\"activity_count_commentlikes\">%d lajků komentáře</string>\n    <string name=\"activity_count_usertags\">%d uživatelských štítků</string>\n    <string name=\"activity_count_likes\">%d se líbí</string>\n    <string name=\"activity_count_poy\">%d fotek s vámi</string>\n    <string name=\"activity_count_requests\">%d žádostí o sledování</string>\n    <string name=\"activity_notloggedin\">Odhlásili jste se před kliknutím na toto oznámení?</string>\n    <string name=\"feed\">Kanál</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Více</string>\n    <string name=\"title_dm\">Zprávy</string>\n    <string name=\"number_selected\">%d vybráno</string>\n    <string name=\"logout_success\">Odhlášení proběhlo úspěšně!</string>\n    <string name=\"dm_thread_info\">Informace</string>\n    <string name=\"mark_as_seen\">Označit jako přečtené</string>\n    <string name=\"version\">Verze</string>\n    <string name=\"pref_start_screen\">Úvodní obrazovka</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Při otevření vyhledávání zobrazit klávesnici</string>\n    <string name=\"pref_category_general\">Obecné</string>\n    <string name=\"pref_category_theme\">Motiv</string>\n    <string name=\"pref_category_downloads\">Stažené</string>\n    <string name=\"pref_category_locale\">Místní prostředí</string>\n    <string name=\"account\">Účet</string>\n    <string name=\"account_hint\">Přestalo fungovat přihlášení? Jednoduše účet přidejte znovu.</string>\n    <string name=\"add_account\">Přidat účet</string>\n    <string name=\"about_category_license\">Licence (pouze anglicky)</string>\n    <string name=\"about_documentation\">Navštivte náš web</string>\n    <string name=\"about_documentation_summary\">Získejte podporu, diskutujte, setkávejte se s ostatními a bavte se!</string>\n    <string name=\"about_repository\">Podívejte se na náš zdrojový kód na GitHub</string>\n    <string name=\"about_repository_summary\">Auditujte, označte hvězdou, nahlaste chyby, přispívejte a bavte se (znovu)!</string>\n    <string name=\"about_feedback\">Poslat zpětnou vazbu e-mailem</string>\n    <string name=\"about_category_3pt\">Zdroje třetích stran</string>\n    <string name=\"reminder\">Upozornění</string>\n    <string name=\"reminder_summary\">Používejte tuto aplikaci odpovědně. Stažené obrázky by měly být použity pouze pro účely povolené platnými zákony.</string>\n    <string name=\"light_white_theme\">Bílá</string>\n    <string name=\"dark_black_theme\">Černá</string>\n    <string name=\"light_theme_settings\">Světlý vzhled</string>\n    <string name=\"dark_theme_settings\">Tmavý vzhled</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Kávová</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Přidáno do Oblíbených!</string>\n    <string name=\"add_to_favorites\">Přidat do Oblíbených</string>\n    <string name=\"accounts\">Účty</string>\n    <string name=\"hashtags\">Hashtagy</string>\n    <string name=\"locations\">Místa</string>\n    <string name=\"unknown\">Neznámý</string>\n    <string name=\"removed_from_favs\">Odebráno z Oblíbených!</string>\n    <string name=\"backup_and_restore\">Záloha a obnovení</string>\n    <string name=\"auto_backup\">Automatické zálohování</string>\n    <string name=\"auto_backup_summary\">Od Android 6 bude funkce zálohování Androidu automaticky nahrávat všechna nastavení aplikace, přihlašovací údaje a oblíbené na Disk Google. Tato záloha může být obnovena po opětovném nainstalování aplikace.</string>\n    <string name=\"auto_backup_warning\">Toto nastavení nemá žádný účinek, pokud nejsou přítomny služby Google Play nebo pokud je automatické zálohování zakázáno v nastavení zařízení. Zakázání zde nevymaže existující zálohy.</string>\n    <string name=\"auto_backup_setting\">Povolit automatické zálohování</string>\n    <string name=\"manual_backup\">Ruční zálohování</string>\n    <string name=\"backup_summary\">Zálohujte nastavení aplikace Barinsta, přihlašovací údaje a/nebo oblíbené položky do prostého textu nebo šifrovaného souboru pro pozdější obnovu.</string>\n    <string name=\"backup_warning\">Pokud zálohujete přihlašovací údaje, zacházejte se souborem jako s důvěrným a uchovejte ho bezpečně!</string>\n    <string name=\"create_backup\">Vytvořit novou zálohu</string>\n    <string name=\"restore_backup\">Obnovit z existující zálohy</string>\n    <string name=\"file_chosen_label\">Soubor:</string>\n    <string name=\"enter_password\">Zadejte heslo</string>\n    <string name=\"select_backup_file\">Vyberte soubor zálohy (.zaai/.backup)</string>\n    <string name=\"apply\">Použít</string>\n    <string name=\"save\">Uložit</string>\n    <string name=\"caption\">Popisek</string>\n    <string name=\"edit_caption\">Upravit popisek</string>\n    <string name=\"translate_caption\">Přeložit popisek</string>\n    <string name=\"player_timeline_desc\">Časová osa přehrávače videa</string>\n    <string name=\"liking\">Lajkování…</string>\n    <string name=\"like_unsuccessful\">Lajkování bylo neúspěšné</string>\n    <string name=\"unlike_unsuccessful\">Odebírání lajku bylo neúspěšné</string>\n    <string name=\"unliking\">Odebírání lajku…</string>\n    <string name=\"controls\">Ovládací prvky</string>\n    <string name=\"saving\">Ukládání…</string>\n    <string name=\"removing\">Odstraňování…</string>\n    <string name=\"save_unsuccessful\">Uložení se nezdařilo</string>\n    <string name=\"save_remove_unsuccessful\">Odstranění se nezdařilo</string>\n    <string name=\"downloading\">Stahování…</string>\n    <string name=\"downloader_downloading_child\">Stahuje se %1$d z %2$d</string>\n    <string name=\"delete\">Smazat</string>\n    <string name=\"comment\">Komentovat</string>\n    <string name=\"layout\">Rozložení</string>\n    <string name=\"feed_stories\">Kanál příběhů</string>\n    <string name=\"opening_post\">Otevírání příspěvku…</string>\n    <string name=\"share\">Sdílet</string>\n    <string name=\"layout_style\">Styl rozložení</string>\n    <string name=\"column_count\">Počet sloupců</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Zobrazit jména</string>\n    <string name=\"show_avatars\">Zobrazit avatary</string>\n    <string name=\"avatar_size\">Velikost avataru</string>\n    <string name=\"corners\">Rohy</string>\n    <string name=\"show_grid_gap\">Zobrazit mezery v mřížce</string>\n    <string name=\"post_not_found\">Příspěvek nebyl nalezen!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galerie</string>\n    <string name=\"camera\">Fotoaparát</string>\n    <string name=\"all_photos\">Všechny fotografie</string>\n    <string name=\"all_media\">Všechna média</string>\n    <string name=\"all_videos\">Všechna videa</string>\n    <string name=\"brightness\">Jas</string>\n    <string name=\"contrast\">Kontrast</string>\n    <string name=\"vibrance\">Živost</string>\n    <string name=\"saturation\">Saturace</string>\n    <string name=\"sharpen\">Zaostření</string>\n    <string name=\"exposure\">Expozice</string>\n    <string name=\"center\">Střed</string>\n    <string name=\"color\">Barva</string>\n    <string name=\"start\">Začátek</string>\n    <string name=\"end\">Konec</string>\n    <string name=\"bilateral_blur\">Bilaterální rozostření</string>\n    <string name=\"vignette\">Vinětace</string>\n    <string name=\"box_blur\">Hranaté rozmazání</string>\n    <string name=\"sepia\">Sépie</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Obnovit</string>\n    <string name=\"crop\">Oříznout</string>\n    <string name=\"normal\">Normální</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d zobrazení</item>\n        <item quantity=\"few\">%d zobrazení</item>\n        <item quantity=\"many\">%d zobrazení</item>\n        <item quantity=\"other\">%d zobrazení</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s příběh</item>\n        <item quantity=\"few\">%s příběhy</item>\n        <item quantity=\"many\">%s příběhů</item>\n        <item quantity=\"other\">%s příběhů</item>\n    </plurals>\n    <string name=\"details\">Podrobnosti</string>\n    <string name=\"title\">Název</string>\n    <string name=\"members\">Členové</string>\n    <string name=\"admin\">Správce</string>\n    <string name=\"inviter\">Hostitel</string>\n    <string name=\"mute_messages\">Ztišit zprávy</string>\n    <string name=\"mute_mentions\">Ztišit zmínky</string>\n    <string name=\"add_members\">Přidat členy</string>\n    <string name=\"search\">Hledat</string>\n    <string name=\"done\">Hotovo</string>\n    <string name=\"dms_action_make_admin\">Učinit správcem</string>\n    <string name=\"dms_action_remove_admin\">Odebrat jako správce</string>\n    <string name=\"edit_unsuccessful\">Úprava se nezdařila</string>\n    <string name=\"message\">Zpráva</string>\n    <string name=\"tap_to_remove\">Klepnutím odebrat</string>\n    <string name=\"forward\">Přeposlat</string>\n    <string name=\"forward_outgoing\">Přeposlali jste zprávu</string>\n    <string name=\"forward_incoming\">Přeposlal(a) zprávu</string>\n    <string name=\"add\">Přidat</string>\n    <string name=\"send\">Odeslat</string>\n    <string name=\"replying_to_yourself\">Odpovídáte sobě</string>\n    <string name=\"replying_to_user\">Odpovídá na %s</string>\n    <string name=\"replied_to_yourself\">Odpověděli jste sami sobě</string>\n    <string name=\"replied_you\">Odpověděli jste</string>\n    <string name=\"replied_you_group\">Odpověděli jste %s</string>\n    <string name=\"replied_group\">Odpovídá %s</string>\n    <string name=\"replied_to_you\">Odpověděl(a) vám</string>\n    <string name=\"replied_to_themself\">Odpověděli sami sobě</string>\n    <string name=\"reacted_story_outgoing\">Reagovali jste na jejich příběh</string>\n    <string name=\"reacted_story_incoming\">Reagoval(a) na váš příběh</string>\n    <string name=\"mentioned_story_outgoing\">Zmínili jste je ve svém příběhu</string>\n    <string name=\"mentioned_story_incoming\">Zmínili vás ve svém příběhu</string>\n    <string name=\"replied_story_outgoing\">Odpověděli jste na jejich příběh</string>\n    <string name=\"replied_story_incoming\">Odpověděli na váš příběh</string>\n    <string name=\"raven_image_expired\">Platnost obrázku vypršela</string>\n    <string name=\"raven_image_info\">Obrázek zmizí po zobrazení</string>\n    <string name=\"raven_video_expired\">Platnost videa vypršela</string>\n    <string name=\"raven_video_info\">Video zmizí po zobrazení</string>\n    <string name=\"raven_msg_expired\">Platnost zprávy vypršela</string>\n    <string name=\"raven_msg_info\">Zpráva zmizí po zobrazení</string>\n    <string name=\"story_share\">Příběh @%s</string>\n    <string name=\"story_share_highlight\">Zvýrazněný příběh @%s</string>\n    <string name=\"photo\">Fotka</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Hlasová zpráva</string>\n    <string name=\"post\">Příspěvek</string>\n    <string name=\"approval_required_for_new_members\">K připojení je nutné schválení</string>\n    <string name=\"requests\">Žádosti</string>\n    <string name=\"admins_only\">Pouze správci</string>\n    <string name=\"added_by\">Přidal(a) %s</string>\n    <string name=\"admin_approval_required\">Vyžadováno schválení správcem</string>\n    <string name=\"admin_approval_required_description\">Pro přidání nových členů do skupiny bude vyžadováno schválení správcem</string>\n    <string name=\"dms_action_end\">Ukončit chat</string>\n    <string name=\"dms_action_end_question\">Ukončit chat?</string>\n    <string name=\"dms_action_end_description\">Všichni členové budou odebráni ze skupiny. Stále budou moci zobrazit historii konverzace.</string>\n    <string name=\"pending_requests\">Nevyřízené žádosti</string>\n    <string name=\"accept_request_from_user\">Přijmout žádost od %1s (%2s)?</string>\n    <string name=\"decline\">Odmítnout</string>\n    <string name=\"accept\">Přijmout</string>\n    <string name=\"you\">Vy</string>\n    <string name=\"no_pending_requests\">Žádné nevyřízené žádosti</string>\n    <string name=\"checking_for_new_messages\">Zjišťování nových zpráv</string>\n    <string name=\"pref_category_stories\">Příběhy</string>\n    <string name=\"pref_category_dm\">Zprávy</string>\n    <string name=\"pref_category_notifications\">Upozornění</string>\n    <string name=\"pref_category_post\">Příspěvky</string>\n    <string name=\"enable_dm_notifications\">Zapnout upozornění na zprávy</string>\n    <string name=\"enable_dm_auto_refesh\">Automaticky obnovovat zprávy</string>\n    <string name=\"auto_refresh_every\">Automaticky obnovit každé</string>\n    <string name=\"secs\">sekundy</string>\n    <string name=\"mins\">minuty</string>\n    <string name=\"search_giphy\">Prohledat GIPHY</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Stav odpovědi není v pořádku!</string>\n    <string name=\"generic_failed_request\">Žádost selhala!</string>\n    <string name=\"hint_keyword\">Klíčové slovo</string>\n    <string name=\"toggle_keyword_filter\">Povolit filtr klíčových slov</string>\n    <string name=\"edit_keyword_filter\">Upravit filtry klíčových slov</string>\n    <string name=\"added_keywords\">Přidáno klíčové slovo: %s na seznam filtrovaných</string>\n    <string name=\"removed_keywords\">Odstraněno klíčové slovo: %s ze seznamu filtrovaných</string>\n    <string name=\"marked_as_seen\">Označeno jako zobrazené</string>\n    <string name=\"delete_unsuccessful\">Odstranění se nezdařilo</string>\n    <string name=\"throttle_error\">Omezeno Instagramem kvůli příliš mnoha požadavkům na API. Před dalším pokusem nějakou dobu počkejte.</string>\n    <string name=\"error\">Chyba</string>\n    <string name=\"account_logged_out\">Tento účet byl odhlášen.</string>\n    <string name=\"login_required\">Vyžadováno přihlášení!</string>\n    <string name=\"inactive_user\">Uživatel je neaktivní!</string>\n    <string name=\"crash_report_subject\">Hlášení o pádu Barinsta</string>\n    <string name=\"crash_report_title\">Vyberte aplikaci e-mailu pro odeslání protokolů o selhání</string>\n    <string name=\"not_found\">Nenalezeno!</string>\n    <string name=\"rate_limit\">Vaše IP adresa byla omezena Instagramem. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Zjistěte více.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Přeskočit tuto aktualizaci</string>\n    <string name=\"on_latest_version\">Již máte nejnovější verzi</string>\n    <string name=\"tab_order\">Pořadí obrazovek</string>\n    <string name=\"other_tabs\">Další karty</string>\n    <string name=\"tab_order_start_next_launch\">Nové pořadí karet se projeví při příštím spuštění</string>\n    <string name=\"dm_remove_warning\">Po uložení budou při příštím spuštění vypnuty všechny funkce související s posíláním zpráv</string>\n    <string name=\"copy_caption\">Kopírovat popisek</string>\n    <string name=\"copy_reply\">Kopírovat odpověď</string>\n    <string name=\"restore\">Obnovit</string>\n    <string name=\"backup\">Zálohovat</string>\n    <string name=\"dir_select_default_message\">Vyberte složku, kam může Barinsta ukládat stažené a dočasné soubory.\\n\\nMůžete to později změnit ve Více &gt; Nastavení &gt; Stažené.</string>\n    <string name=\"dir_select_reselect_message\">Android změnil způsob, jakým mohou aplikace přistupovat k souborům a adresářům v úložišti. V současné době nemá Barinsta oprávnění k přístupu k následující složce:</string>\n    <string name=\"dir_select_permission_revoked_message\">Oprávnění pro dříve vybranou složku byla systémem zrušena:</string>\n    <string name=\"dir_select_folder_not_exist\">Dříve vybraná složka už neexistuje:</string>\n    <string name=\"dir_select_message2\">Znovu vyberte adresář nebo vyberte nový adresář kliknutím na tlačítko níže.</string>\n    <string name=\"select_a_folder\">Není vybrána žádná složka!</string>\n    <string name=\"dir_select_no_download_folder\">Vyberte prosím adresář z vašeho úložiště, ne kategorii na postranním panelu.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Úspěch! Počkejte prosím. Spouštění aplikace…</string>\n    <string name=\"barinsta_folder\">Složka Barinsta</string>\n    <string name=\"top\">Nejlepší</string>\n    <string name=\"recent\">Nedávné</string>\n    <string name=\"clear\">Vymazat</string>\n    <string name=\"no_external_map_app\">Nemáte žádnou aplikaci pro mapy!</string>\n    <string name=\"click_to_show_full\">Klepnutím zobrazíte přesný počet \\\"to se mi líbí\\\"</string>\n    <string name=\"no_profile_pic_found\">Nebyl nalezen žádný obrázek profilu!</string>\n    <string name=\"swipe_up_confirmation\">Opravdu chcete otevřít tento odkaz?</string>\n    <string name=\"sending\">Odesílání…</string>\n    <string name=\"share_via_dm\">Sdílet do zprávy</string>\n    <string name=\"share_link\">Sdílet odkaz…</string>\n    <string name=\"slide_to_cancel\">Posunutím zrušíte</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-de/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Systemstandard</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automatisch (System)</item>\n        <item>Automatisch (Akku)</item>\n        <item>Dunkel</item>\n        <item>Hell</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram Standard (ungelesen, dann gelesen)</item>\n        <item>Vom Neuesten zum Ältesten</item>\n        <item>Vom Ältesten zum Neuesten</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Kein(e)</item>\n        <item>\\@</item>\n        <item>um</item>\n        <item>am</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>Sek.</item>\n        <item>Min.</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Über</string>\n    <string name=\"action_dms\">Private Nachrichten</string>\n    <string name=\"action_settings\">Einstellungen</string>\n    <string name=\"action_download\">Herunterladen</string>\n    <string name=\"action_search\">Benutzernamen suchen…</string>\n    <string name=\"action_compare\">Vergleichen</string>\n    <string name=\"clipboard_error\">Fehler beim Kopieren des Textes</string>\n    <string name=\"clipboard_copied\">In die Zwischenablage kopiert!</string>\n    <string name=\"report\">Melden</string>\n    <string name=\"set_password\">Datei mit Passwort schützen</string>\n    <string name=\"password_no_max\">Passwort</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Ja</string>\n    <string name=\"cancel\">Abbrechen</string>\n    <string name=\"no\">Nein</string>\n    <string name=\"confirm\">Bestätigen</string>\n    <string name=\"title_favorites\">Favoriten</string>\n    <string name=\"title_discover\">Entdecken</string>\n    <string name=\"title_comments\">Kommentare</string>\n    <string name=\"title_replies\">Antworten</string>\n    <string name=\"title_notifications\">Aktivität</string>\n    <string name=\"update_check\">Beim Start auf Updates prüfen</string>\n    <string name=\"flag_secure\">Bildschirmfotos &amp; App-Vorschau verhindern</string>\n    <string name=\"download_user_folder\">Beiträge in Benutzernamen-Ordner herunterladen</string>\n    <string name=\"download_prepend_username\">Benutzername dem Dateinamen voranstellen</string>\n    <string name=\"mark_as_seen_setting\">Stories nach dem Ansehen als gesehen markieren</string>\n    <string name=\"mark_as_seen_setting_summary\">Die Person wird wissen, dass du dir die Story angesehen hast</string>\n    <string name=\"hide_muted_reels_setting\">Stummgeschaltete Stories im Feed ausblenden</string>\n    <string name=\"dm_mark_as_seen_setting\">Direktnachrichten nach dem Ansehen als gesehen markieren</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Andere Mitglieder werden wissen, dass du sie gesehen hast</string>\n    <string name=\"autoplay_stories_setting\">Videos in Stories automatisch wiedergeben</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Aktivitätsbenachrichtigungen aktivieren</string>\n    <string name=\"story_sort_setting\">Sortierung des Storyfeeds</string>\n    <string name=\"error_loading_profile\">Fehler beim Laden des Profils! Ist der Benutzername gültig? Wenn ja, könnte es sein, dass deine Zugriffe begrenzt werden.</string>\n    <string name=\"error_loading_hashtag\">Fehler beim Laden des Hashtags! Ist der Name gültig?</string>\n    <string name=\"error_loading_location\">Fehler beim Laden der Position! Ist die URL gültig?</string>\n    <string name=\"error_creating_folders\">Fehler beim Erstellen von Download-Ordner(n).</string>\n    <string name=\"select_folder\">Ordner auswählen</string>\n    <string name=\"theme_settings\">Design</string>\n    <string name=\"select_language\">Sprache</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nBeitrag</item>\n        <item quantity=\"other\">%s\\nBeiträge</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Beitrag</item>\n        <item quantity=\"other\">%s Beiträge</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nAbonnent</item>\n        <item quantity=\"other\">%s\\nAbonnenten</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nAbonniert</string>\n    <string name=\"post_viewer_autoplay_video\">Videos automatisch abspielen</string>\n    <string name=\"post_viewer_background_play\">Videos im Hintergrund fortführen</string>\n    <string name=\"post_viewer_background_play_summary\">Videos nicht pausieren, wenn die App nicht aktiv ist</string>\n    <string name=\"post_viewer_muted_autoplay\">Videos immer stummschalten</string>\n    <string name=\"post_viewer_show_captions\">Bildunterschrift immer anzeigen</string>\n    <string name=\"post_viewer_download_dialog_title\">Datei zum Download auswählen</string>\n    <string name=\"post_viewer_download_current\">Aktuell</string>\n    <string name=\"post_viewer_download_album\">Gesamtes Album</string>\n    <string name=\"show_stories\">Stories anzeigen</string>\n    <string name=\"no_more_stories\">Keine Stories mehr!</string>\n    <string name=\"view_post\">Beitrag anzeigen</string>\n    <string name=\"story_poll\">Umfrage</string>\n    <string name=\"answered_story\">Antwort erfolgreich!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d Antwort im Durchschnitt %s</item>\n        <item quantity=\"other\">%d Antworten im Durchschnitt %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Deine Antwort: %s</string>\n    <string name=\"reply_story\">Auf Story antworten</string>\n    <string name=\"reply_hint\">Antworten…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Schieberegler</string>\n    <string name=\"story_quizzed\">Du hast bereits geantwortet!</string>\n    <string name=\"story_mentions\">Erwähnungen</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Dieser Account ist privat</string>\n    <string name=\"priv_acc_confirm\">Du wirst nicht mehr auf Beiträge zugreifen können, wenn du entfolgst! Bist du sicher?</string>\n    <string name=\"are_you_sure\">Bist du sicher?</string>\n    <string name=\"no_acc\">Du kannst dich unten rechts über Mehr -&gt; anmelden, oder öffentliche Konten ohne Anmeldung ansehen!</string>\n    <string name=\"empty_acc\">Dieses Konto hat keine Beiträge</string>\n    <string name=\"empty_list\">Keine derartigen Posts!</string>\n    <string name=\"login\">Anmelden</string>\n    <string name=\"logout\">Abmelden</string>\n    <string name=\"logout_summary\">Instagram anonym durchsuchen</string>\n    <string name=\"remove_all_acc\">Alle Konten entfernen</string>\n    <string name=\"remove_all_acc_warning\">Dadurch werden alle hinzugefügten Konten aus der App entfernt!\\nUm nur ein Konto zu entfernen, tippe lange auf das Konto im Dialogfeld für den Kontowechsel.\\nMöchtest du fortfahren?</string>\n    <string name=\"time_settings\">Datumsformat</string>\n    <string name=\"saved_create_collection\">Neue Sammlung anlegen</string>\n    <string name=\"edit_collection\">Sammlungsname bearbeiten</string>\n    <string name=\"delete_collection\">Sammlung löschen</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Zur Sammlung hinzufügen…</string>\n    <string name=\"remove_from_collection\">Aus Sammlung entfernen</string>\n    <string name=\"liked\">Gefällt mir</string>\n    <string name=\"saved\">Gespeichert</string>\n    <string name=\"tagged\">Markiert</string>\n    <string name=\"dm_person\">Nachricht</string>\n    <string name=\"follow\">Folgen</string>\n    <string name=\"unfollow\">Nicht mehr folgen</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorit</string>\n    <string name=\"block\">Blockieren</string>\n    <string name=\"unblock\">Nicht mehr blockieren</string>\n    <string name=\"restrict\">Einschränken</string>\n    <string name=\"unrestrict\">Nicht mehr einschränken</string>\n    <string name=\"mute_stories\">Stories stummschalten</string>\n    <string name=\"mute_posts\">Beiträge stummschalten</string>\n    <string name=\"unmute_stories\">Stummschaltung von Stories aufheben</string>\n    <string name=\"unmute_posts\">Stummschaltung von Beiträgen aufheben</string>\n    <string name=\"remove_follower\">Abonnent entfernen</string>\n    <string name=\"bio_copy\">Steckbrief kopieren</string>\n    <string name=\"bio_translate\">Steckbrief übersetzen</string>\n    <string name=\"status_mutual\">Gegenseitig</string>\n    <string name=\"status_following\">Abonniert</string>\n    <string name=\"status_follower\">Abonnent</string>\n    <string name=\"map\">Karte</string>\n    <string name=\"dialog_export_accounts\">Konten</string>\n    <string name=\"dialog_export_settings\">Einstellungen</string>\n    <string name=\"dialog_export_favorites\">Favoriten</string>\n    <string name=\"dialog_import_success\">Erfolgreich importiert!</string>\n    <string name=\"dialog_import_failed\">Importieren fehlgeschlagen!</string>\n    <string name=\"dialog_export_success\">Erfolgreich exportiert!</string>\n    <string name=\"dialog_export_failed\">Export fehlgeschlagen!</string>\n    <string name=\"refresh\">Aktualisieren</string>\n    <string name=\"get_cookies\">Cookies abrufen</string>\n    <string name=\"time_settings_title_custom\">Eigenes Format verwenden</string>\n    <string name=\"time_settings_title_separator\">Trennzeichen</string>\n    <string name=\"time_settings_title_time_format\">Zeiformat</string>\n    <string name=\"time_settings_title_date_format\">Datumsformat</string>\n    <string name=\"time_settings_title_preview\">Vorschau</string>\n    <string name=\"time_settings_swap_time\">Zeit und Datum tauschen</string>\n    <string name=\"quick_access_cannot_delete_curr\">Derzeit verwendetes Konto kann nicht gelöscht werden</string>\n    <string name=\"quick_access_confirm_delete\">Bist du sicher, dass du \\'%s \\' löschen möchtest?</string>\n    <string name=\"open_profile\">Profil öffnen</string>\n    <string name=\"view_story\">Story ansehen</string>\n    <string name=\"view_pfp\">Profilbild ansehen</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Nicht unterstützter Nachrichtentyp</string>\n    <string name=\"dms_inbox_unsend\">Nachricht zurückrufen</string>\n    <string name=\"dms_inbox_giphy\">Auf GIPHY ansehen</string>\n    <string name=\"dms_inbox_shared_post\">%s hat einen Beitrag von @%s geteilt</string>\n    <string name=\"dms_inbox_shared_image\">%s hat ein Bild geteilt</string>\n    <string name=\"dms_inbox_shared_video\">%s hat ein Video geteilt</string>\n    <string name=\"dms_inbox_shared_message\">%s hat eine Nachricht gesendet</string>\n    <string name=\"dms_inbox_shared_gif\">%s hat ein GIF geteilt</string>\n    <string name=\"dms_inbox_shared_sticker\">%s hat einen Sticker geteilt</string>\n    <string name=\"dms_inbox_shared_profile\">%s hat ein Profil geteilt: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s hat einen Standort geteilt: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s teilte ein Story Highlight von @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s teilte eine Story von @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s hat eine Sprachnachricht gesendet</string>\n    <string name=\"dms_inbox_shared_clip\">%s teilte einen Clip von @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s teile ein IGTV Video von @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Du hast auf ihre Story geantwortet: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s antwortete auf deine Story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Du hast auf ihre Story reagiert: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reagierte auf deine Story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Du hast @%s in deiner Story erwähnt</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s erwähnte dich in einer Story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unbekannter Medientyp</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Medien abgelaufen!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Zugestellt</string>\n    <string name=\"dms_inbox_raven_media_sent\">Gesendet</string>\n    <string name=\"dms_inbox_raven_media_opened\">Geöffnet</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Abgespielt</string>\n    <string name=\"dms_inbox_raven_media_sending\">Wird gesendet…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blockiert</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Empfohlen</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshot erstellt</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Kann nicht zugestellt werden</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Für Audioaufnahme drücken und halten</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Chat verlassen</string>\n    <string name=\"dms_action_leave_question\">Chat verlassen?</string>\n    <string name=\"dms_action_kick\">Entfernen</string>\n    <string name=\"dms_left_users\">Ehemalige Benutzer</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Ungültiger Benutzer</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram erlaubt das Hochladen von Videos, die länger als 60 Sek. sind, nicht für PN.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram erlaubt das Hochladen von Audiodateien, die länger als 60 Sek. sind, nicht.</string>\n    <string name=\"direct_download_loading\">Beiträgen abrufen</string>\n    <string name=\"downloader_complete\">Download abgeschlossen</string>\n    <string name=\"downloader_preparing\">Download wird vorbereitet…</string>\n    <string name=\"downloader_downloading_post\">Beitrag wird heruntergeladen…</string>\n    <string name=\"downloader_downloading_media\">Medien herunterladen</string>\n    <string name=\"downloader_unknown_error\">Unbekannter Fehler ist aufgetreten!!!</string>\n    <string name=\"downloader_error_creating_folder\">Fehler beim Erstellen des Ordners!</string>\n    <string name=\"downloader_error_download_file\">Fehler beim Dateidownload</string>\n    <string name=\"comment_viewer_translate_comment\">Kommentar übersetzen</string>\n    <string name=\"comment_viewer_delete_comment\">Kommentar löschen</string>\n    <string name=\"followers_type_followers\">Abonnenten</string>\n    <string name=\"followers_type_following\">Abonniert</string>\n    <string name=\"followers_compare\">Vergleiche Abonnenten &amp; abonnierte Accounts</string>\n    <string name=\"followers_both_following\">Gegenseitig abonniert</string>\n    <string name=\"followers_not_following\">Hat %s nicht abonniert</string>\n    <string name=\"followers_not_follower\">Nicht von %s abonniert</string>\n    <string name=\"login_error_loading_cookies\">Fehler beim Laden der Cookies</string>\n    <string name=\"comment_hint\">Schreibe einen neuen Kommentar…</string>\n    <string name=\"liked_notif\">gefällt dein Foto.</string>\n    <string name=\"comment_notif\">Hat deinen Beitrag kommentiert:</string>\n    <string name=\"follow_notif\">folgt dir jetzt.</string>\n    <string name=\"tagged_notif\">Hat dich in einem Beitrag getaggt</string>\n    <string name=\"request_notif\">Angefordert dir zu folgen</string>\n    <string name=\"request_approve\">Anfrage annehmen</string>\n    <string name=\"request_reject\">Anfrage ablehnen</string>\n    <string name=\"share_public_post\">Diesen öffentlichen Beitrag teilen mit…</string>\n    <string name=\"share_private_post\">Dies ist ein privater Beitrag! Teile ihn mit denen, die ihn sehen können.</string>\n    <string name=\"discover_empty\">Diese Kategorie ist leer…</string>\n    <string name=\"update_available\">Ein Update ist verfügbar! (%s)</string>\n    <string name=\"updated\">Danke für die Aktualisierung von Barinsta!</string>\n    <string name=\"crash_title\">App abgestürzt</string>\n    <string name=\"crash_descr\">Hoppla.. die App ist abgestürzt, aber keine Sorge — du kannst Fehlerberichte an den Entwickler senden, um ihm zu helfen, das Problem zu beheben. (:</string>\n    <string name=\"action_notif\">Aktivität</string>\n    <string name=\"action_archive\">Story-Archiv</string>\n    <string name=\"action_ayml\">Vorgeschlagene Benutzer</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d Abonnenten</string>\n    <string name=\"activity_count_comments\">%d Kommentare</string>\n    <string name=\"activity_count_commentlikes\">%d gelikte Kommentare</string>\n    <string name=\"activity_count_usertags\">%d Benutzertags</string>\n    <string name=\"activity_count_likes\">%d Likes</string>\n    <string name=\"activity_count_poy\">%d Fotos von dir</string>\n    <string name=\"activity_count_requests\">%d Anfragen dir zu folgen</string>\n    <string name=\"activity_notloggedin\">Hast du dich abgemeldet, bevor du auf diese Benachrichtigung geklickt hast?</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Mehr</string>\n    <string name=\"title_dm\">PN</string>\n    <string name=\"number_selected\">%d ausgewählt</string>\n    <string name=\"logout_success\">Erfolgreich abgemeldet!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Als gesehen markieren</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Startbildschirm</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Tastatur bei der Suche anzeigen</string>\n    <string name=\"pref_category_general\">Allgemein</string>\n    <string name=\"pref_category_theme\">Design</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Sprache</string>\n    <string name=\"account\">Account</string>\n    <string name=\"account_hint\">Aktueller Login funktioniert nicht? Füge das Konto erneut hinzu.</string>\n    <string name=\"add_account\">Account hinzufügen</string>\n    <string name=\"about_category_license\">Lizenz (nur auf Englisch)</string>\n    <string name=\"about_documentation\">Besuche unsere Webseite</string>\n    <string name=\"about_documentation_summary\">Bekomme Unterstützung, diskutiere, treffe andere und hab Spaß!</string>\n    <string name=\"about_repository\">Quellcode auf GitHub einsehen</string>\n    <string name=\"about_repository_summary\">Prüfe den Code, melde Fehler, werde Teil des Projekts und hab (nochmal) Spaß!</string>\n    <string name=\"about_feedback\">Feedback per E-Mail senden</string>\n    <string name=\"about_category_3pt\">Integration von Drittanbietern</string>\n    <string name=\"reminder\">Erinnerung</string>\n    <string name=\"reminder_summary\">Bitte verwende diese App verantwortungsvoll. Heruntergeladene Bilder sollten nur für Zwecke verwendet werden, die durch die geltenden Gesetze erlaubt sind.</string>\n    <string name=\"light_white_theme\">Weiß</string>\n    <string name=\"dark_black_theme\">Schwarz</string>\n    <string name=\"light_theme_settings\">Helles Design</string>\n    <string name=\"dark_theme_settings\">Dunkles Design</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Zu Favoriten hinzugefügt!</string>\n    <string name=\"add_to_favorites\">Zu Favoriten hinzufügen</string>\n    <string name=\"accounts\">Konten</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Standorte</string>\n    <string name=\"unknown\">Unbekannt</string>\n    <string name=\"removed_from_favs\">Aus Favoriten entfernt!</string>\n    <string name=\"backup_and_restore\">Sichern &amp; Wiederherstellen</string>\n    <string name=\"auto_backup\">Automatische Sicherung</string>\n    <string name=\"auto_backup_summary\">Seit Android 6 lädt die automatische Sicherungsfunktion von Android alle App-Einstellungen, Konto-Anmeldedaten und Favoriten auf dein Google Drive hoch, die nach der Deinstallation durch eine Neuinstallation der App wiederhergestellt werden können.</string>\n    <string name=\"auto_backup_warning\">Diese Einstellung hat keine Auswirkung, wenn Google Play Services nicht vorhanden ist oder wenn die automatische Sicherung in den Geräteeinstellungen deaktiviert ist. Wenn du diese Einstellung deaktivierst, werden vorhandene Sicherungen nicht gelöscht.</string>\n    <string name=\"auto_backup_setting\">Automatische Sicherung aktivieren</string>\n    <string name=\"manual_backup\">Manuelle Sicherung</string>\n    <string name=\"backup_summary\">Sichere die Einstellungen der Barinsta-App, die Anmeldedaten des Kontos und/oder die Favoriten in einer Klartext- oder verschlüsselten Sicherungsdatei zur späteren Wiederherstellung.</string>\n    <string name=\"backup_warning\">Wenn du die Anmeldedaten eines Kontos sicherst, behandle die Datei vertraulich und bewahre sie an einem sicheren Ort auf!</string>\n    <string name=\"create_backup\">Neue Sicherungsdatei erstellen</string>\n    <string name=\"restore_backup\">Aus vorhandener Sicherungsdatei wiederherstellen</string>\n    <string name=\"file_chosen_label\">Datei:</string>\n    <string name=\"enter_password\">Passwort eingeben</string>\n    <string name=\"select_backup_file\">Wähle eine Sicherungsdatei (.zaai/.backup)</string>\n    <string name=\"apply\">Anwenden</string>\n    <string name=\"save\">Speichern</string>\n    <string name=\"caption\">Bildunterschrift</string>\n    <string name=\"edit_caption\">Bildunterschrift bearbeiten</string>\n    <string name=\"translate_caption\">Bildunterschrift übersetzen</string>\n    <string name=\"player_timeline_desc\">Zeitleiste des Videoplayers</string>\n    <string name=\"liking\">Gefällt…</string>\n    <string name=\"like_unsuccessful\">Hinzufügen fehlgeschlagen</string>\n    <string name=\"unlike_unsuccessful\">Entfernen fehlgeschlagen</string>\n    <string name=\"unliking\">…entfernen</string>\n    <string name=\"controls\">Steuerung</string>\n    <string name=\"saving\">Speichern…</string>\n    <string name=\"removing\">Wird entfernt…</string>\n    <string name=\"save_unsuccessful\">Speichern fehlgeschlagen</string>\n    <string name=\"save_remove_unsuccessful\">Entfernen fehlgeschlagen</string>\n    <string name=\"downloading\">Herunterladen…</string>\n    <string name=\"downloader_downloading_child\">Element %1$d von %2$d herunterladen</string>\n    <string name=\"delete\">Löschen</string>\n    <string name=\"comment\">Kommentar</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Storyfeed</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Teilen</string>\n    <string name=\"layout_style\">Layoutstil</string>\n    <string name=\"column_count\">Spaltenanzahl</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Namen anzeigen</string>\n    <string name=\"show_avatars\">Avatare anzeigen</string>\n    <string name=\"avatar_size\">Avatargröße</string>\n    <string name=\"corners\">Ecken</string>\n    <string name=\"show_grid_gap\">Rasterabstand anzeigen</string>\n    <string name=\"post_not_found\">Beitrag nicht gefunden!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galerie</string>\n    <string name=\"camera\">Kamera</string>\n    <string name=\"all_photos\">Alle Fotos</string>\n    <string name=\"all_media\">Alle Medien</string>\n    <string name=\"all_videos\">Alle Videos</string>\n    <string name=\"brightness\">Helligkeit</string>\n    <string name=\"contrast\">Kontrast</string>\n    <string name=\"vibrance\">Farblebendigkeit</string>\n    <string name=\"saturation\">Sättigung</string>\n    <string name=\"sharpen\">Schärfe</string>\n    <string name=\"exposure\">Belichtung</string>\n    <string name=\"center\">Zentrieren</string>\n    <string name=\"color\">Farbe</string>\n    <string name=\"start\">Beginn</string>\n    <string name=\"end\">Ende</string>\n    <string name=\"bilateral_blur\">Bilaterale Weichzeichnung</string>\n    <string name=\"vignette\">Vignettierung</string>\n    <string name=\"box_blur\">Box-Unschärfe</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Zurücksetzen</string>\n    <string name=\"crop\">Zuschneiden</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d mal angesehen</item>\n        <item quantity=\"other\">%d mal angesehen</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s Story</item>\n        <item quantity=\"other\">%s Stories</item>\n    </plurals>\n    <string name=\"details\">Details</string>\n    <string name=\"title\">Titel</string>\n    <string name=\"members\">Mitglieder</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Einladender</string>\n    <string name=\"mute_messages\">Nachrichten stummschalten</string>\n    <string name=\"mute_mentions\">Erwähnungen stummschalten</string>\n    <string name=\"add_members\">Mitglieder hinzufügen</string>\n    <string name=\"search\">Suchen</string>\n    <string name=\"done\">Fertig</string>\n    <string name=\"dms_action_make_admin\">Zum Administrator machen</string>\n    <string name=\"dms_action_remove_admin\">Als Administrator entfernen</string>\n    <string name=\"edit_unsuccessful\">Bearbeiten fehlgeschlagen</string>\n    <string name=\"message\">Nachricht</string>\n    <string name=\"tap_to_remove\">Tippe zum Entfernen</string>\n    <string name=\"forward\">Weiterleiten</string>\n    <string name=\"forward_outgoing\">Du hast eine Nachricht weitergeleitet</string>\n    <string name=\"forward_incoming\">Nachricht weitergeleitet</string>\n    <string name=\"add\">Hinzufügen</string>\n    <string name=\"send\">Senden</string>\n    <string name=\"replying_to_yourself\">Antworten auf dich selbst</string>\n    <string name=\"replying_to_user\">Antworten auf %s</string>\n    <string name=\"replied_to_yourself\">Du hast dir selbst geantwortet</string>\n    <string name=\"replied_you\">Du hast geantwortet</string>\n    <string name=\"replied_you_group\">Du hast auf %s geantwortet</string>\n    <string name=\"replied_group\">Geantwortet auf %s</string>\n    <string name=\"replied_to_you\">Dir geantortet</string>\n    <string name=\"replied_to_themself\">Sich selbst geantwortet</string>\n    <string name=\"reacted_story_outgoing\">Du hast auf ihre Story reagiert</string>\n    <string name=\"reacted_story_incoming\">Auf deine Story reagiert</string>\n    <string name=\"mentioned_story_outgoing\">Du hast diese in deiner Story erwähnt</string>\n    <string name=\"mentioned_story_incoming\">Erwähnen dich in ihrer Story</string>\n    <string name=\"replied_story_outgoing\">Du hast auf ihre Story geantwortet</string>\n    <string name=\"replied_story_incoming\">Auf deine Story geantwortet</string>\n    <string name=\"raven_image_expired\">Bild ist abgelaufen</string>\n    <string name=\"raven_image_info\">Bild verfällt sobald es angesehen wurde</string>\n    <string name=\"raven_video_expired\">Video ist abgelaufen</string>\n    <string name=\"raven_video_info\">Video verfällt sobald es angesehen wurde</string>\n    <string name=\"raven_msg_expired\">Nachricht ist abgelaufen</string>\n    <string name=\"raven_msg_info\">Nachricht verfällt, sobald sie gesehen wird</string>\n    <string name=\"story_share\">\\@%s\\'s Story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s Story Highlight</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Sprachnachricht</string>\n    <string name=\"post\">Posten</string>\n    <string name=\"approval_required_for_new_members\">Genehmigung erforderlich um beizutreten</string>\n    <string name=\"requests\">Anfragen</string>\n    <string name=\"admins_only\">Nur Admins</string>\n    <string name=\"added_by\">Hinzugefügt von %s</string>\n    <string name=\"admin_approval_required\">Admin-Genehmigung erforderlich</string>\n    <string name=\"admin_approval_required_description\">Eine Admin-Genehmigung wird benötigt, um neue Mitglieder zur Gruppe hinzuzufügen</string>\n    <string name=\"dms_action_end\">Chat beenden</string>\n    <string name=\"dms_action_end_question\">Chat beenden?</string>\n    <string name=\"dms_action_end_description\">Alle Mitglieder werden aus der Gruppe entfernt, jedoch können sie den Chatverlauf weiterhin einsehen.</string>\n    <string name=\"pending_requests\">Ausstehende Anfragen</string>\n    <string name=\"accept_request_from_user\">Anfrage von %1s (%2s) akzeptieren?</string>\n    <string name=\"decline\">Ablehnen</string>\n    <string name=\"accept\">Aktzeptieren</string>\n    <string name=\"you\">Du</string>\n    <string name=\"no_pending_requests\">Keine ausstehenden Anfragen</string>\n    <string name=\"checking_for_new_messages\">Auf neue Nachrichten prüfen</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">PN</string>\n    <string name=\"pref_category_notifications\">Benachrichtigungen</string>\n    <string name=\"pref_category_post\">Beitrag</string>\n    <string name=\"enable_dm_notifications\">PN-Benachrichtigungen aktivieren</string>\n    <string name=\"enable_dm_auto_refesh\">Lade neue Nachrichten automatisch</string>\n    <string name=\"auto_refresh_every\">Automatisch neuladen alle</string>\n    <string name=\"secs\">Sek.</string>\n    <string name=\"mins\">Min.</string>\n    <string name=\"search_giphy\">GIPHY durchsuchen</string>\n    <string name=\"generic_null_response\">Antwort ist Null!</string>\n    <string name=\"generic_not_ok_response\">Antwortstatus ist nicht in Ordnung!</string>\n    <string name=\"generic_failed_request\">Anfrage fehlgeschlagen!</string>\n    <string name=\"hint_keyword\">Suchbegriff</string>\n    <string name=\"toggle_keyword_filter\">Aktiviere Suchbegriff-Filter</string>\n    <string name=\"edit_keyword_filter\">Suchbegriff-Filter bearbeiten</string>\n    <string name=\"added_keywords\">Suchbegriff: %s zur Filterliste hinzugefügt</string>\n    <string name=\"removed_keywords\">Suchbegriff: %s aus der Filterliste entfernt</string>\n    <string name=\"marked_as_seen\">Als gesehen markieren</string>\n    <string name=\"delete_unsuccessful\">Löschen fehlgeschlagen</string>\n    <string name=\"throttle_error\">Aufgrund von zu vielen API-Anfragen von Instagram gedrosselt. Warte eine Zeit lang, bevor du es erneut versuchst.</string>\n    <string name=\"error\">Fehler</string>\n    <string name=\"account_logged_out\">Dieses Konto wurde abgemeldet.</string>\n    <string name=\"login_required\">Anmeldung erforderlich!</string>\n    <string name=\"inactive_user\">Benutzer ist inaktiv!</string>\n    <string name=\"crash_report_subject\">Barinsta Absturzbericht</string>\n    <string name=\"crash_report_title\">Wähle eine E-Mail-App zum Senden von Absturzprotokollen</string>\n    <string name=\"not_found\">Nicht gefunden!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Dieses Update überspringen</string>\n    <string name=\"on_latest_version\">Du bist bereits auf der neuesten Version</string>\n    <string name=\"tab_order\">Reihenfolge der Anzeige</string>\n    <string name=\"other_tabs\">Andere Registerkarten</string>\n    <string name=\"tab_order_start_next_launch\">Die Reihenfolge der Registerkarten wird beim nächsten Start übernommen</string>\n    <string name=\"dm_remove_warning\">Wenn übernommen, werden alle PN-bezogenen Funktionen beim nächsten Start deaktiviert</string>\n    <string name=\"copy_caption\">Bildunterschrift kopieren</string>\n    <string name=\"copy_reply\">Antwort kopieren</string>\n    <string name=\"restore\">Wiederherstellen</string>\n    <string name=\"backup\">Sichern</string>\n    <string name=\"dir_select_default_message\">Wähle einen Ordner, in dem Barinsta Downloads und temporäre Dateien speichern kann.\\n\\nDu kannst dies später unter Mehr &gt; Einstellungen &gt; Downloads ändern.</string>\n    <string name=\"dir_select_reselect_message\">Android hat die Art und Weise geändert, wie Apps auf Dateien und Verzeichnisse im Speicher zugreifen können. Derzeit hat Barinsta keine Berechtigung für den Zugriff auf den folgenden Ordner:</string>\n    <string name=\"dir_select_permission_revoked_message\">Die Berechtigungen für den zuvor ausgewählten Ordner wurden vom System entzogen:</string>\n    <string name=\"dir_select_folder_not_exist\">Der zuvor ausgewählte Ordner existiert nicht mehr:</string>\n    <string name=\"dir_select_message2\">Wähle das Verzeichnis erneut aus oder wähle ein neues Verzeichnis, indem du unten auf die Schaltfläche klickst.</string>\n    <string name=\"select_a_folder\">Kein Ordner ausgewählt!</string>\n    <string name=\"dir_select_no_download_folder\">Bitte wähle ein Verzeichnis aus deinem Speicher, nicht eine Kategorie in der Seitenleiste.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Erfolg! Bitte warten. App wird gestartet…</string>\n    <string name=\"barinsta_folder\">Barinsta-Ordner</string>\n    <string name=\"top\">Anfang</string>\n    <string name=\"recent\">Neueste</string>\n    <string name=\"clear\">Leeren</string>\n    <string name=\"no_external_map_app\">Keine Karten-App gefunden!</string>\n    <string name=\"click_to_show_full\">Klicken, um die gesamte Anzahl der Likes anzuzeigen</string>\n    <string name=\"no_profile_pic_found\">Kein Profilbild gefunden!</string>\n    <string name=\"swipe_up_confirmation\">Bist du sicher, dass du diesen Link öffnen möchtest?</string>\n    <string name=\"sending\">Sende…</string>\n    <string name=\"share_via_dm\">Über PN teilen</string>\n    <string name=\"share_link\">Link teilen…</string>\n    <string name=\"slide_to_cancel\">Zum Abbrechen wischen</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-el/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Προεπιλογή Συστήματος</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Αυτόματα / Ακολούθηση Συστήματος</item>\n        <item>Αυτόματα / Ακολούθηση Μπαταρίας</item>\n        <item>Σκούρο</item>\n        <item>Φωτεινό</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Προεπιλογή του Instagram (Μη αναγνωσμένα και μετά αναγνωσμένα)</item>\n        <item>Από το νεότερο στο παλαιότερο</item>\n        <item>Από το παλαιότερο στο νεότερο</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Κανένα</item>\n        <item>\\@</item>\n        <item>στις</item>\n        <item>την</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>δεύτερα</item>\n        <item>λεπτά</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Σχετικά με</string>\n    <string name=\"action_dms\">Απευθείας Μηνύματα</string>\n    <string name=\"action_settings\">Ρυθμίσεις</string>\n    <string name=\"action_download\">Λήψη</string>\n    <string name=\"action_search\">Αναζήτηση ονόματος χρήστη…</string>\n    <string name=\"action_compare\">Σύγκριση</string>\n    <string name=\"clipboard_error\">Σφάλμα κατά την αντιγραφή κειμένου</string>\n    <string name=\"clipboard_copied\">Αντιγράφηκε στο πρόχειρο!</string>\n    <string name=\"report\">Αναφορά</string>\n    <string name=\"set_password\">Προστασία αρχείου με κωδικό πρόσβασης</string>\n    <string name=\"password_no_max\">Κωδικός Πρόσβασης</string>\n    <string name=\"ok\">Εντάξει</string>\n    <string name=\"yes\">Ναι</string>\n    <string name=\"cancel\">Ακύρωση</string>\n    <string name=\"no\">Όχι</string>\n    <string name=\"confirm\">Επιβεβαίωση</string>\n    <string name=\"title_favorites\">Αγαπημένα</string>\n    <string name=\"title_discover\">Ανακαλύψτε</string>\n    <string name=\"title_comments\">Σχόλια</string>\n    <string name=\"title_replies\">Απαντήσεις</string>\n    <string name=\"title_notifications\">Δραστηριότητα</string>\n    <string name=\"update_check\">Έλεγχος για ενημερώσεις κατά την εκκίνηση</string>\n    <string name=\"flag_secure\">Παρεμπόδιση στιγμιοτύπων οθόνης &amp; προεπισκόπησης εφαρμογής</string>\n    <string name=\"download_user_folder\">Λήψη δημοσιεύσεων σε φακέλους ονομάτων χρηστών</string>\n    <string name=\"download_prepend_username\">Προσθέστε το όνομα χρήστη πριν από το όνομα του αρχείου</string>\n    <string name=\"mark_as_seen_setting\">Επισήμανση ιστοριών ως προβληθέντων μετά την προβολή</string>\n    <string name=\"mark_as_seen_setting_summary\">Ο δημιουργός της ιστορίας θα γνωρίζει ότι προβλήθηκε</string>\n    <string name=\"hide_muted_reels_setting\">Απόκρυψη ιστοριών που βρίσκονται σε σίγαση από τη ροή</string>\n    <string name=\"dm_mark_as_seen_setting\">Επισήμανση μηνυμάτων ως αναγνωσμένων μετά την προβολή</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Τα υπόλοιπα μέλη θα γνωρίζουν ότι προβλήθηκε</string>\n    <string name=\"autoplay_stories_setting\">Αυτόματη αναπαραγωγή βίντεο ιστοριών</string>\n    <string name=\"story_list_setting\">Εμφάνιση λίστας ιστοριών από προεπιλογή</string>\n    <string name=\"story_list_setting_summary\">Για την προβολή ιστοριών</string>\n    <string name=\"activity_setting\">Ενεργοποίηση ειδοποιήσεων δραστηριότητας</string>\n    <string name=\"story_sort_setting\">Ταξινόμηση ροής ιστορίων</string>\n    <string name=\"error_loading_profile\">Σφάλμα κατά τη φόρτωση προφίλ! Είναι το όνομα χρήστη έγκυρο; Αν ναι, μπορεί να είστε περιορισμένος.</string>\n    <string name=\"error_loading_hashtag\">Σφάλμα κατά τη φόρτωση hashtag! Είναι το όνομα έγκυρο;</string>\n    <string name=\"error_loading_location\">Σφάλμα κατά την φόρτωση τοποθεσίας! Είναι η διεύθυνση έγκυρη;</string>\n    <string name=\"error_creating_folders\">Σφάλμα κατά τη δημιουργία φακέλου/-ων λήψης.</string>\n    <string name=\"select_folder\">Επιλογή φακέλου</string>\n    <string name=\"theme_settings\">Θέμα</string>\n    <string name=\"select_language\">Γλώσσα</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nΔημοσίευση</item>\n        <item quantity=\"other\">%s\\nΔημοσιεύσεις</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Δημοσίευση</item>\n        <item quantity=\"other\">%s Δημοσιεύσεις</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nΑκόλουθος</item>\n        <item quantity=\"other\">%s\\nΑκόλουθοι</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nΑκολουθείτε</string>\n    <string name=\"post_viewer_autoplay_video\">Αυτόματη αναπαραγωγή των βίντεο</string>\n    <string name=\"post_viewer_background_play\">Συνέχεια βίντεο στο παρασκήνιο</string>\n    <string name=\"post_viewer_background_play_summary\">Να μην γίνεται παύση των βίντεο όταν η εφαρμογή είναι εκτός εστίασης</string>\n    <string name=\"post_viewer_muted_autoplay\">Μόνιμη σίγαση των βίντεο</string>\n    <string name=\"post_viewer_show_captions\">Μόνιμη εμφάνιση των λεζαντών των δημοσιεύσεων</string>\n    <string name=\"post_viewer_download_dialog_title\">Επιλογή δημοσιεύσεων για λήψη</string>\n    <string name=\"post_viewer_download_current\">Τρέχουσα</string>\n    <string name=\"post_viewer_download_album\">Πλήρη συλλογή</string>\n    <string name=\"show_stories\">Εμφάνιση ιστοριών</string>\n    <string name=\"no_more_stories\">Δεν υπάρχουν άλλες ιστορίες!</string>\n    <string name=\"view_post\">Προβολή Δημοσίευσης</string>\n    <string name=\"story_poll\">Δημοσκόπηση</string>\n    <string name=\"answered_story\">Η απάντηση ήταν επιτυχής!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d απόκριση κατά μέσο όρο%s</item>\n        <item quantity=\"other\">%d αποκρίσεις κατά μέσο όρο %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Η απάντησή σας: %s</string>\n    <string name=\"reply_story\">Απάντηση στην ιστορία</string>\n    <string name=\"reply_hint\">Απάντηση…</string>\n    <string name=\"story_quiz\">Κουίζ</string>\n    <string name=\"story_slider\">Slider</string>\n    <string name=\"story_quizzed\">Έχετε ήδη απαντήσει!</string>\n    <string name=\"story_mentions\">Αναφορές</string>\n    <string name=\"story_question\">Ερώτηση</string>\n    <string name=\"priv_acc\">Ιδιωτικός λογαριασμός</string>\n    <string name=\"priv_acc_confirm\">Δε θα έχετε πρόσβαση στις δημοσιεύσεις μετά την άρση ακολούθησης! Είστε βέβαιος;</string>\n    <string name=\"are_you_sure\">Είστε σίγουρος;</string>\n    <string name=\"no_acc\">Μπορείτε να συνδεθείτε μέσω του Περισσότερα-&gt; Λογαριασμός στην κάτω δεξιά γωνία ή μπορείτε να δείτε δημόσιους λογαριασμούς χωρίς σύνδεση!</string>\n    <string name=\"empty_acc\">Αυτός ο λογαριασμός δεν έχει δημοσιεύσεις</string>\n    <string name=\"empty_list\">Δεν Υπάρχουν Τέτοιες Δημοσιεύσεις!</string>\n    <string name=\"login\">Σύνδεση</string>\n    <string name=\"logout\">Αποσύνδεση</string>\n    <string name=\"logout_summary\">Ανώνυμη περιήγηση στο Instagram</string>\n    <string name=\"remove_all_acc\">Αφαίρεση όλων των λογαριασμών</string>\n    <string name=\"remove_all_acc_warning\">Έτσι, θα αφαιρεθούν όλοι οι λογαριασμοί που έχουν προστεθεί στην εφαρμογή!\\nΓια να αφαιρέσετε μόνο έναν λογαριασμό, πατήστε τον παρατεταμένα από τον διάλογο εναλλαγής λογαριασμών.\\nΘέλετε να συνεχίσετε;</string>\n    <string name=\"time_settings\">Μορφή ημερομηνίας</string>\n    <string name=\"saved_create_collection\">Δημιουργία νέας συλλογής</string>\n    <string name=\"edit_collection\">Επεξεργασία ονόματος συλλογής</string>\n    <string name=\"delete_collection\">Διαγραφή συλλογής</string>\n    <string name=\"delete_collection_note\">Όλες οι δημοσιεύσεις που περιέχονται στη διαγραμμένη συλλογή θα παραμείνουν σε άλλες συλλογές.</string>\n    <string name=\"add_to_collection\">Προσθήκη στη συλλογή…</string>\n    <string name=\"remove_from_collection\">Αφαίρεση από τη συλλογή</string>\n    <string name=\"liked\">Μ\\'αρέσουν</string>\n    <string name=\"saved\">Αποθηκευμένα</string>\n    <string name=\"tagged\">Ετικέτες</string>\n    <string name=\"dm_person\">Μήνυμα</string>\n    <string name=\"follow\">Ακολουθήστε</string>\n    <string name=\"unfollow\">Να μην ακολουθώ</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Αγαπημένα</string>\n    <string name=\"block\">Αποκλεισμός</string>\n    <string name=\"unblock\">Άρση αποκλεισμού</string>\n    <string name=\"restrict\">Περιορισμός</string>\n    <string name=\"unrestrict\">Άρση Περιορισμού</string>\n    <string name=\"mute_stories\">Σίγαση ιστοριών</string>\n    <string name=\"mute_posts\">Σίγαση δημοσιεύσεων</string>\n    <string name=\"unmute_stories\">Άρση σίγασης ιστοριών</string>\n    <string name=\"unmute_posts\">Άρση σίγασης δημοσιεύσεων</string>\n    <string name=\"remove_follower\">Αφαίρεση ακόλουθου</string>\n    <string name=\"bio_copy\">Αντιγραφή βιογραφικού</string>\n    <string name=\"bio_translate\">Μετάφραση βιογραφικού</string>\n    <string name=\"status_mutual\">Κοινά</string>\n    <string name=\"status_following\">Ακολουθείτε</string>\n    <string name=\"status_follower\">Ακόλουθος</string>\n    <string name=\"map\">Χάρτης</string>\n    <string name=\"dialog_export_accounts\">Λογαριασμοί</string>\n    <string name=\"dialog_export_settings\">Ρυθμίσεις</string>\n    <string name=\"dialog_export_favorites\">Αγαπημένα</string>\n    <string name=\"dialog_import_success\">Η εισαγωγή ήταν επιτυχής!</string>\n    <string name=\"dialog_import_failed\">Η εισαγωγή απέτυχε!</string>\n    <string name=\"dialog_export_success\">Η εξαγωγή ήταν επιτυχής!</string>\n    <string name=\"dialog_export_failed\">Η εξαγωγή απέτυχε!</string>\n    <string name=\"refresh\">Ανανέωση</string>\n    <string name=\"get_cookies\">Λήψη cookies</string>\n    <string name=\"time_settings_title_custom\">Χρήση προσαρμοσμένης μορφής</string>\n    <string name=\"time_settings_title_separator\">Διαχωριστικό</string>\n    <string name=\"time_settings_title_time_format\">Μορφή Ώρας</string>\n    <string name=\"time_settings_title_date_format\">Μορφή Ημερομηνίας</string>\n    <string name=\"time_settings_title_preview\">Προεπισκόπηση</string>\n    <string name=\"time_settings_swap_time\">Εναλλαγή θέσεων ώρας και ημερομηνίας</string>\n    <string name=\"quick_access_cannot_delete_curr\">Αδυναμία διαγραφής λογαριασμού που χρησιμοποιείται αυτή την στιγμή</string>\n    <string name=\"quick_access_confirm_delete\">Είστε βέβαιος ότι θέλετε να διαγράψετε το «%s»;</string>\n    <string name=\"open_profile\">Άνοιγμα προφίλ</string>\n    <string name=\"view_story\">Προβολή ιστορίας</string>\n    <string name=\"view_pfp\">Προβολή εικόνας προφίλ</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Μη υποστηριζόμενος τύπος μηνύματος</string>\n    <string name=\"dms_inbox_unsend\">Κατάργηση αποστολής μηνύματος</string>\n    <string name=\"dms_inbox_giphy\">Προβολή στο GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s κοινοποίησε μια δημοσίευση από @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s κοινοποίησε μια εικόνα</string>\n    <string name=\"dms_inbox_shared_video\">%s κοινοποίησε ένα βίντεο</string>\n    <string name=\"dms_inbox_shared_message\">%s έστειλε ένα μήνυμα</string>\n    <string name=\"dms_inbox_shared_gif\">%s κοινοποίησε ένα gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s κοινοποίησε ένα αυτοκόλλητο</string>\n    <string name=\"dms_inbox_shared_profile\">%s κοινοποίησε ένα προφίλ: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s κοινοποίησε μία τοποθεσία: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s κοινοποίησε ένα highlight ιστορίας από @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s κοινοποίησε μία ιστορία από @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s έστειλε ένα ηχητικό μήνυμα</string>\n    <string name=\"dms_inbox_shared_clip\">%s κοινοποίησε ένα κλιπ από @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s κοινοποίησε ένα IGTV βίντεο από @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Απαντήσατε στην ιστορία του: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s απάντησε στην ιστορία σας: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Αντιδράσατε στην ιστορία του: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s αντέδρασε στην ιστορία σας: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Αναφέρατε τον χρήστη @%s στην ιστορία σας</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s σας ανέφερε στην ιστορία τους</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Άγνωστος τύπος πολυμέσου</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Το πολυμέσο έχει λήξει!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Παραδόθηκε</string>\n    <string name=\"dms_inbox_raven_media_sent\">Απεστάλη</string>\n    <string name=\"dms_inbox_raven_media_opened\">Ανοίχτηκε</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Αναπαράχθηκε ξανά</string>\n    <string name=\"dms_inbox_raven_media_sending\">Γίνεται Αποστολή…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Αποκλείστηκε</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Προτεινόμενα</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Λήφθηκε στιγμιότυπο οθόνης</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Αδυναμία παράδοσης</string>\n    <string name=\"dms_thread_message_hint\">Μήνυμα…</string>\n    <string name=\"dms_thread_audio_hint\">Πατήστε παρατεταμένα για εγγραφή ήχου</string>\n    <string name=\"dms_thread_updating\">Ενημέρωση…</string>\n    <string name=\"dms_action_leave\">Αποχώρηση από τη συνομιλία</string>\n    <string name=\"dms_action_leave_question\">Αποχώρηση από αυτήν τη συνομιλία;</string>\n    <string name=\"dms_action_kick\">Διώξιμο</string>\n    <string name=\"dms_left_users\">Χρήστες που αποχώρησαν</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Μη έγκυρος χρήστης</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Το Instagram δεν επιτρέπει τη αναφόρτωση βίντεο άνω των 60 δευτ. ως μηνύματα.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Το Instagram δεν επιτρέπει τη αναφόρτωση αρχείων ήχου άνω των 60 δευτ.</string>\n    <string name=\"direct_download_loading\">Ανάκτηση δημοσίευσης/-εων</string>\n    <string name=\"downloader_complete\">Η λήψη ολοκληρώθηκε</string>\n    <string name=\"downloader_preparing\">Προετοιμασία για λήψη…</string>\n    <string name=\"downloader_downloading_post\">Γίνεται λήψη της δημοσίευσης…</string>\n    <string name=\"downloader_downloading_media\">Γίνεται λήψη πολυμέσων</string>\n    <string name=\"downloader_unknown_error\">Προέκυψε ένα άγνωστο σφάλμα!!!</string>\n    <string name=\"downloader_error_creating_folder\">Σφάλμα κατά τη δημιουργία φακέλου!</string>\n    <string name=\"downloader_error_download_file\">Σφάλμα λήψης αρχείου</string>\n    <string name=\"comment_viewer_translate_comment\">Μετάφραση σχολίου</string>\n    <string name=\"comment_viewer_delete_comment\">Διαγραφή σχολίου</string>\n    <string name=\"followers_type_followers\">Ακόλουθοι</string>\n    <string name=\"followers_type_following\">Ακολουθείτε</string>\n    <string name=\"followers_compare\">Σύγκριση ακολούθων</string>\n    <string name=\"followers_both_following\">Ακολουθείτε ο ένας τον άλλον</string>\n    <string name=\"followers_not_following\">δεν ακολουθεί %s</string>\n    <string name=\"followers_not_follower\">%s δεν ακολουθεί</string>\n    <string name=\"login_error_loading_cookies\">Σφάλμα κατά την φόρτωση των cookies</string>\n    <string name=\"comment_hint\">Σύνταξη νέου σχολίου…</string>\n    <string name=\"liked_notif\">Δήλωσε ότι του αρέσει η δημοσίευσή σας</string>\n    <string name=\"comment_notif\">Σχολίασε στη δημοσίευση σας:</string>\n    <string name=\"follow_notif\">Ξεκίνησε να σας ακολουθεί</string>\n    <string name=\"tagged_notif\">Σας πρόσθεσε με ετικέτα σε μια δημοσίευση</string>\n    <string name=\"request_notif\">Ζήτησε να σας ακολουθήσει</string>\n    <string name=\"request_approve\">Έγκριση αιτήματος</string>\n    <string name=\"request_reject\">Απόρριψη αιτήματος</string>\n    <string name=\"share_public_post\">Κοινοποιήστε αυτή τη δημόσια δημοσίευση στο…</string>\n    <string name=\"share_private_post\">Ιδιωτική δημοσίευση! Κοινοποιήστε την σε όσους μπορούν να τη δουν.</string>\n    <string name=\"discover_empty\">Αυτή η κατηγορία είναι κατά κάποιον τρόπο κενή…</string>\n    <string name=\"update_available\">Υπάρχει διαθέσιμη ενημέρωση! (%s)</string>\n    <string name=\"updated\">Ευχαριστούμε που ενημερώσατε το Barinsta!</string>\n    <string name=\"crash_title\">Η εφαρμογή κατέρρευσε</string>\n    <string name=\"crash_descr\">Ώπα.. Η εφαρμογή κατέρρευσε, αλλά μην ανησυχείτε. Μπορείτε να αναφέρετε το σφάλμα στον προγραμματιστή, για να τον βοηθήσετε να διορθώσει το πρόβλημα. (:</string>\n    <string name=\"action_notif\">Δραστηριότητα</string>\n    <string name=\"action_archive\">Αρχειοθήκη ιστοριών</string>\n    <string name=\"action_ayml\">Προτεινόμενοι χρήστες</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">Εχετε %d ειδοποίηση</item>\n        <item quantity=\"other\">Έχετε %d ειδοποιήσεις</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d ακόλουθοι</string>\n    <string name=\"activity_count_comments\">%d σχόλια</string>\n    <string name=\"activity_count_commentlikes\">Το σχόλιο αρέσει σε %d</string>\n    <string name=\"activity_count_usertags\">%d ετικέτες χρήστη</string>\n    <string name=\"activity_count_likes\">Αρέσει σε %d</string>\n    <string name=\"activity_count_poy\">%d φωτογραφίες σας</string>\n    <string name=\"activity_count_requests\">%d αιτήματα ακολούθησης</string>\n    <string name=\"activity_notloggedin\">Αποσυνδεθήκατε προτού πατήσετε αυτήν την ειδοποίηση;!</string>\n    <string name=\"feed\">Ροή</string>\n    <string name=\"profile\">Προφίλ</string>\n    <string name=\"more\">Λοιπά</string>\n    <string name=\"title_dm\">Μηνύματα</string>\n    <string name=\"number_selected\">Επιλέχθηκαν %d</string>\n    <string name=\"logout_success\">Η αποσύνδεση ήταν επιτυχής!</string>\n    <string name=\"dm_thread_info\">Πληροφορίες</string>\n    <string name=\"mark_as_seen\">Σήμανση ως αναγνωσμένο</string>\n    <string name=\"version\">Έκδοση</string>\n    <string name=\"pref_start_screen\">Οθόνη εκκίνησης</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Εμφάνιση πληκτρολογίου στην αναζήτηση</string>\n    <string name=\"pref_category_general\">Γενικά</string>\n    <string name=\"pref_category_theme\">Θέμα</string>\n    <string name=\"pref_category_downloads\">Λήψεις</string>\n    <string name=\"pref_category_locale\">Τοποθεσία</string>\n    <string name=\"account\">Λογαριασμός</string>\n    <string name=\"account_hint\">Δε λειτουργεί η τρέχουσα σύνδεση; Απλά προσθέστε ξανά τον λογαριασμό.</string>\n    <string name=\"add_account\">Προσθήκη λογαριασμού</string>\n    <string name=\"about_category_license\">Άδεια (μόνο στα Αγγλικά)</string>\n    <string name=\"about_documentation\">Επισκεφθείτε τον ιστότοπό μας</string>\n    <string name=\"about_documentation_summary\">Λάβετε υποστήριξη, συζητήστε, συναντήστε άλλους και διασκεδάστε!</string>\n    <string name=\"about_repository\">Δείτε τον πηγαίο κώδικά μας στο GitHub</string>\n    <string name=\"about_repository_summary\">Έλεγχος, αναφορά σφαλμάτων, συνεισφορά και διασκέδαση (ξανά)!</string>\n    <string name=\"about_feedback\">Αποστολή σχολίων μέσω ηλ. ταχυδρομείου</string>\n    <string name=\"about_category_3pt\">Αποδόσεις Τρίτων</string>\n    <string name=\"reminder\">Υπενθύμιση</string>\n    <string name=\"reminder_summary\">Παρακαλούμε χρησιμοποιήστε αυτήν την εφαρμογή υπεύθυνα. Οι ληφθείσες εικόνες πρέπει να χρησιμοποιούνται μόνο για σκοπούς που επιτρέπονται από τους ισχύοντες νόμους.</string>\n    <string name=\"light_white_theme\">Λευκό</string>\n    <string name=\"dark_black_theme\">Μαύρο</string>\n    <string name=\"light_theme_settings\">Φωτεινό θέμα</string>\n    <string name=\"dark_theme_settings\">Σκούρο θέμα</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Κόκκοι καφέ</string>\n    <string name=\"dark_material_dark_theme\">Σκουρόχρωμο</string>\n    <string name=\"added_to_favs\">Προστέθηκε στα Αγαπημένα!</string>\n    <string name=\"add_to_favorites\">Στα αγαπημένα</string>\n    <string name=\"accounts\">Λογαριασμοί</string>\n    <string name=\"hashtags\">Hashtag</string>\n    <string name=\"locations\">Τοποθεσίες</string>\n    <string name=\"unknown\">Άγνωστο</string>\n    <string name=\"removed_from_favs\">Αφαίρεση από τα αγαπημένα!</string>\n    <string name=\"backup_and_restore\">Αντίγραφα ασφαλείας &amp; Επαναφορά</string>\n    <string name=\"auto_backup\">Αυτόματη δημιουργία αντιγράφων ασφαλείας</string>\n    <string name=\"auto_backup_summary\">Από το Android 6 κι έπειτα, το χαρακτηριστικό αυτόματης δημιουργίας αντιγράφων ασφαλείας του Android, θα αναφορτώνει όλες τις ρυθμίσεις της εφαρμογής, τα δεδομένα σύνδεσης του λογαριασμού και τα αγαπημένα στο Google Drive σας. Έτσι, θα μπορούν να επαναφερθούν με την επανεγκατάσταση της εφαρμογής μετά την απεγκατάσταση.</string>\n    <string name=\"auto_backup_warning\">Αυτή η προτίμηση δεν έχει καμία επίδραση εάν οι υπηρεσίες Google Play δεν είναι παρούσες, ή εάν το αυτόματο αντίγραφο ασφαλείας είναι απενεργοποιημένο από τις ρυθμίσεις της συσκευής σας. Η απενεργοποίηση εδώ δεν διαγράφει τα υπάρχοντα αντίγραφα.</string>\n    <string name=\"auto_backup_setting\">Ενεργοποίηση Αυτόματης Δημιουργίας Αντιγράφων Ασφαλείας</string>\n    <string name=\"manual_backup\">Μη αυτόματη δημιουργία αντιγράφων ασφαλείας</string>\n    <string name=\"backup_summary\">Δημιουργία αντιγράφου ασφαλείας των ρυθμίσεων της εφαρμογής, των δεδομένων σύνδεσης του λογαριασμού και/ή των αγαπημένων, σε ακρυπτογράφητο ή κρυπτογραφημένο αρχείο για μεταγενέστερη επαναφορά.</string>\n    <string name=\"backup_warning\">Αν δημιουργείτε αντίγραφα ασφαλείας των δεδομένων σύνδεσης λογαριασμού, αντιμετωπίστε το αρχείο ως απόρρητο και κρατήστε το σε ασφαλές μέρος!</string>\n    <string name=\"create_backup\">Δημιουργία νέου αρχείου αντιγράφου ασφαλείας</string>\n    <string name=\"restore_backup\">Επαναφορά από υπάρχον αρχείο αντιγράφου ασφαλείας</string>\n    <string name=\"file_chosen_label\">Αρχείο:</string>\n    <string name=\"enter_password\">Καταχώριση κωδικού πρόσβασης</string>\n    <string name=\"select_backup_file\">Επιλέξτε ένα αρχείο αντιγράφου ασφαλείας (.zaai/.backup)</string>\n    <string name=\"apply\">Εφαρμογή</string>\n    <string name=\"save\">Αποθήκευση</string>\n    <string name=\"caption\">Λεζάντα</string>\n    <string name=\"edit_caption\">Επεξεργασία λεζάντας</string>\n    <string name=\"translate_caption\">Μετάφραση λεζάντας</string>\n    <string name=\"player_timeline_desc\">Χρονοδιάγραμμα αναπαραγωγού βίντεο</string>\n    <string name=\"liking\">«Μου αρέσει»…</string>\n    <string name=\"like_unsuccessful\">Ανεπιτυχής επισήμανση ως «Μου Αρέσει»</string>\n    <string name=\"unlike_unsuccessful\">Ανεπιτυχής αναίρεση του «Μου Αρέσει»</string>\n    <string name=\"unliking\">Αναίρεση «Μου Αρέσει»…</string>\n    <string name=\"controls\">Στοιχεία ελέγχου</string>\n    <string name=\"saving\">Αποθήκευση…</string>\n    <string name=\"removing\">Αφαίρεση…</string>\n    <string name=\"save_unsuccessful\">Η αποθήκευση ήταν ανεπιτυχής</string>\n    <string name=\"save_remove_unsuccessful\">Η αφαίρεση ήταν ανεπιτυχής</string>\n    <string name=\"downloading\">Λήψη…</string>\n    <string name=\"downloader_downloading_child\">Λήψη στοιχείου %1$d από %2$d</string>\n    <string name=\"delete\">Διαγραφή</string>\n    <string name=\"comment\">Σχόλιο</string>\n    <string name=\"layout\">Διάταξη</string>\n    <string name=\"feed_stories\">Ροή ιστοριών</string>\n    <string name=\"opening_post\">Άνοιγμα δημοσίευσης…</string>\n    <string name=\"share\">Κοινοποίηση</string>\n    <string name=\"layout_style\">Τρόπος διάταξης</string>\n    <string name=\"column_count\">Πλήθος στηλών</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Εμφάνιση ονομάτων</string>\n    <string name=\"show_avatars\">Εμφάνιση εικόνας προφίλ</string>\n    <string name=\"avatar_size\">Μέγεθος εικόνας προφίλ</string>\n    <string name=\"corners\">Γωνίες</string>\n    <string name=\"show_grid_gap\">Εμφάνιση κενού πλέγματος</string>\n    <string name=\"post_not_found\">Δε βρέθηκε η δημοσίευση!</string>\n    <string name=\"no_external_app_url\">Δεν βρέθηκαν εφαρμογές για άνοιγμα διευθύνσεων URL</string>\n    <string name=\"gallery\">Συλλογή</string>\n    <string name=\"camera\">Κάμερα</string>\n    <string name=\"all_photos\">Όλες οι φωτογραφίες</string>\n    <string name=\"all_media\">Όλα τα μέσα</string>\n    <string name=\"all_videos\">Όλα τα βίντεο</string>\n    <string name=\"brightness\">Φωτεινότητα</string>\n    <string name=\"contrast\">Αντίθεση</string>\n    <string name=\"vibrance\">Ζωντάνια</string>\n    <string name=\"saturation\">Κορεσμός</string>\n    <string name=\"sharpen\">Όξυνση</string>\n    <string name=\"exposure\">Έκθεση</string>\n    <string name=\"center\">Κέντρο</string>\n    <string name=\"color\">Χρώμα</string>\n    <string name=\"start\">Έναρξη</string>\n    <string name=\"end\">Τέλος</string>\n    <string name=\"bilateral_blur\">Διμερές Θόλωμα</string>\n    <string name=\"vignette\">Βινιετάρισμα</string>\n    <string name=\"box_blur\">Θόλωμα πλαισίου</string>\n    <string name=\"sepia\">Σέπια</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Επαναφορά</string>\n    <string name=\"crop\">Περικοπή</string>\n    <string name=\"normal\">Κανονικό</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d προβολή</item>\n        <item quantity=\"other\">%d προβολές</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s ιστορία</item>\n        <item quantity=\"other\">%s ιστορίες</item>\n    </plurals>\n    <string name=\"details\">Λεπτομέρειες</string>\n    <string name=\"title\">Τίτλος</string>\n    <string name=\"members\">Μέλη</string>\n    <string name=\"admin\">Διαχειριστής</string>\n    <string name=\"inviter\">Προσκαλών</string>\n    <string name=\"mute_messages\">Σίγαση μηνυμάτων</string>\n    <string name=\"mute_mentions\">Σίγαση αναφορών</string>\n    <string name=\"add_members\">Προσθήκη μελών</string>\n    <string name=\"search\">Αναζήτηση</string>\n    <string name=\"done\">Έγινε</string>\n    <string name=\"dms_action_make_admin\">Θέστε ως διαχειριστή</string>\n    <string name=\"dms_action_remove_admin\">Κατάργηση ως Διαχειριστής</string>\n    <string name=\"edit_unsuccessful\">Η επεξεργασία απέτυχε</string>\n    <string name=\"message\">Μήνυμα</string>\n    <string name=\"tap_to_remove\">Πατήστε για αφαίρεση</string>\n    <string name=\"forward\">Προώθηση</string>\n    <string name=\"forward_outgoing\">Προωθήσατε ένα μήνυμα</string>\n    <string name=\"forward_incoming\">Προωθήθηκε ένα μήνυμα</string>\n    <string name=\"add\">Προσθήκη</string>\n    <string name=\"send\">Αποστολή</string>\n    <string name=\"replying_to_yourself\">Απάντηση στον εαυτό σας</string>\n    <string name=\"replying_to_user\">Απάντηση σε %s</string>\n    <string name=\"replied_to_yourself\">Απαντήσατε στον εαυτό σας</string>\n    <string name=\"replied_you\">Απαντήσατε</string>\n    <string name=\"replied_you_group\">Απαντήσατε στο %s</string>\n    <string name=\"replied_group\">Απαντήθηκε στον %s</string>\n    <string name=\"replied_to_you\">Σας απάντησε</string>\n    <string name=\"replied_to_themself\">Απάντησε στον εαυτό του</string>\n    <string name=\"reacted_story_outgoing\">Αντιδράσατε στην ιστορία του</string>\n    <string name=\"reacted_story_incoming\">Αντέδρασε στην ιστορία σας</string>\n    <string name=\"mentioned_story_outgoing\">Τον αναφέρατε στην ιστορία σας</string>\n    <string name=\"mentioned_story_incoming\">Σας ανέφερε στην ιστορία του</string>\n    <string name=\"replied_story_outgoing\">Απαντήσατε στην ιστορία του</string>\n    <string name=\"replied_story_incoming\">Απάντησε στην ιστορία σας</string>\n    <string name=\"raven_image_expired\">Η εικόνα έληξε</string>\n    <string name=\"raven_image_info\">Η εικόνα θα λήξει όταν προβληθεί</string>\n    <string name=\"raven_video_expired\">Το βίντεο έληξε</string>\n    <string name=\"raven_video_info\">Το βίντεο θα λήξει όταν προβληθεί</string>\n    <string name=\"raven_msg_expired\">Το μήνυμα έληξε</string>\n    <string name=\"raven_msg_info\">Το μήνυμα θα λήξει όταν προβληθεί</string>\n    <string name=\"story_share\">Ιστορία του @%s</string>\n    <string name=\"story_share_highlight\">Highlight ιστορίας του @%s</string>\n    <string name=\"photo\">Φωτογραφία</string>\n    <string name=\"video\">Βίντεο</string>\n    <string name=\"voice_message\">Ηχητικό μήνυμα</string>\n    <string name=\"post\">Δημοσίευση</string>\n    <string name=\"approval_required_for_new_members\">Απαιτείται έγκριση για ένταξη</string>\n    <string name=\"requests\">Αιτήματα</string>\n    <string name=\"admins_only\">Μόνο οι διαχειριστές</string>\n    <string name=\"added_by\">Προστέθηκε από %s</string>\n    <string name=\"admin_approval_required\">Απαιτείται έγκριση διαχειριστή</string>\n    <string name=\"admin_approval_required_description\">Απαιτείται έγκριση διαχειριστή για την προσθήκη νέων μελών στην ομάδα</string>\n    <string name=\"dms_action_end\">Τερματισμός συνομιλίας</string>\n    <string name=\"dms_action_end_question\">Τερματισμός συνομιλίας;</string>\n    <string name=\"dms_action_end_description\">Όλα τα μέλη θα αφαιρεθούν από την ομάδα. Θα μπορούν ακόμα να δουν το ιστορικό συνομιλίας.</string>\n    <string name=\"pending_requests\">Αιτήματα σε Εκκρεμότητα</string>\n    <string name=\"accept_request_from_user\">Αποδοχή αιτήματος από %1s (%2s);</string>\n    <string name=\"decline\">Απόρριψη</string>\n    <string name=\"accept\">Αποδοχή</string>\n    <string name=\"you\">Εσείς</string>\n    <string name=\"no_pending_requests\">Δεν υπάρχουν εκκρεμή αιτήματα</string>\n    <string name=\"checking_for_new_messages\">Γίνεται έλεγχος για νέα μηνύματα</string>\n    <string name=\"pref_category_stories\">Ιστορίες</string>\n    <string name=\"pref_category_dm\">Μηνύματα</string>\n    <string name=\"pref_category_notifications\">Ειδοποιήσεις</string>\n    <string name=\"pref_category_post\">Δημοσίευση</string>\n    <string name=\"enable_dm_notifications\">Ενεργοποίηση ειδοποιήσεων μηνυμάτων</string>\n    <string name=\"enable_dm_auto_refesh\">Αυτόματη ανανέωση μηνυμάτων</string>\n    <string name=\"auto_refresh_every\">Αυτόματη ανανέωση κάθε</string>\n    <string name=\"secs\">δεύτερα</string>\n    <string name=\"mins\">λεπτά</string>\n    <string name=\"search_giphy\">Αναζήτηση στο GIPHY</string>\n    <string name=\"generic_null_response\">Άκυρη απόκριση!</string>\n    <string name=\"generic_not_ok_response\">Η κατάσταση απόκρισης δεν είναι εντάξει!</string>\n    <string name=\"generic_failed_request\">Αποτυχία αιτήματος!</string>\n    <string name=\"hint_keyword\">Λέξη-κλειδί</string>\n    <string name=\"toggle_keyword_filter\">Ενεργοποίηση φίλτρου λέξεων-κλειδιών</string>\n    <string name=\"edit_keyword_filter\">Επεξεργασία φίλτρων λέξεων-κλειδιών</string>\n    <string name=\"added_keywords\">Προστέθηκε λέξη-κλειδί: %s στον κατάλογο φιλτραρίσματος</string>\n    <string name=\"removed_keywords\">Αφαιρέθηκε λέξη-κλειδί: %s στον κατάλογο φιλτραρίσματος</string>\n    <string name=\"marked_as_seen\">Επισήμανθηκε ως αναγνωσμένο</string>\n    <string name=\"delete_unsuccessful\">Η διαγραφή απέτυχε</string>\n    <string name=\"throttle_error\">Περιορίστηκατε από το Instagram λόγω υπερβολικών αιτήσεων API. Περιμένετε ορισμένη ώρα προτού προσπαθήσετε ξανά.</string>\n    <string name=\"error\">Σφάλμα</string>\n    <string name=\"account_logged_out\">Αυτός ο λογαριασμός έχει αποσυνδεθεί.</string>\n    <string name=\"login_required\">Απαιτείται σύνδεση!</string>\n    <string name=\"inactive_user\">Ο χρήστης είναι ανενεργός!</string>\n    <string name=\"crash_report_subject\">Αναφορά Κατάρρευσης Barinsta</string>\n    <string name=\"crash_report_title\">Επιλέξτε μια εφαρμογή ηλ. ταχυδρομείου για αποστολή αρχείων καταγραφής κατάρρευσης</string>\n    <string name=\"not_found\">Δε βρέθηκε!</string>\n    <string name=\"rate_limit\">Η διεύθυνση IP σας έχει περιοριστεί από το Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Μάθετε περισσότερα.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Παράλειψη της ενημέρωσης</string>\n    <string name=\"on_latest_version\">Η εφαρμογή είναι ήδη στην τελευταία έκδοση</string>\n    <string name=\"tab_order\">Σειρά της οθόνης</string>\n    <string name=\"other_tabs\">Λοιπές καρτέλες</string>\n    <string name=\"tab_order_start_next_launch\">Η σειρά των καρτελών θα ισχύσει από την επόμενη εκκίνηση</string>\n    <string name=\"dm_remove_warning\">Εάν αποθηκευτεί, όλες οι λειτουργίες που είναι σχετικές με τα Μηνύματα, θα είναι απενεργοποιημένες στην επόμενη εκκίνηση</string>\n    <string name=\"copy_caption\">Αντιγραφή λεζάντας</string>\n    <string name=\"copy_reply\">Αντιγραφή απάντησης</string>\n    <string name=\"restore\">Ανάκτηση</string>\n    <string name=\"backup\">Αντίγραφο ασφαλείας</string>\n    <string name=\"dir_select_default_message\">Επιλέξτε έναν φάκελο όπου εκεί θα αποθηκεύονται οι λήψεις και τα προσωρινά αρχεία του Barinsta.\\n\\nΗ επιλογή σας μπορεί να αλλαχθεί στο Λοιπά&gt; Ρυθμίσεις&gt; Λήψεις.</string>\n    <string name=\"dir_select_reselect_message\">Το Android έχει αλλάξει τον τρόπο με τον οποίο οι εφαρμογές μπορούν να έχουν πρόσβαση σε αρχεία και καταλόγους στον αποθηκευτικό χώρο. Αυτή τη στιγμή η Barinsta δεν έχει άδεια πρόσβασης στον ακόλουθο φάκελο:</string>\n    <string name=\"dir_select_permission_revoked_message\">Τα δικαιώματα για τον προηγουμένως επιλεγμένο φάκελο ανακλήθηκαν από το σύστημα:</string>\n    <string name=\"dir_select_folder_not_exist\">Ο προηγουμένως επιλεγμένος φάκελος δεν υπάρχει τώρα:</string>\n    <string name=\"dir_select_message2\">Επιλέξτε ξανά τον φάκελο ή επιλέξτε ένα νέο φάκελο κάνοντας κλικ στο παρακάτω κουμπί.</string>\n    <string name=\"select_a_folder\">Δεν επιλέχθηκε φάκελος!</string>\n    <string name=\"dir_select_no_download_folder\">Παρακαλώ επιλέξτε έναν φάκελο από τον αποθηκευτικό σας χώρο, όχι μια κατηγορία στην πλαϊνή μπάρα.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Επιτυχία! Παρακαλώ περιμένετε. Εκκίνηση εφαρμογής…</string>\n    <string name=\"barinsta_folder\">Φάκελος Barinsta</string>\n    <string name=\"top\">Κορυφή</string>\n    <string name=\"recent\">Πρόσφατα</string>\n    <string name=\"clear\">Εκκαθάριση</string>\n    <string name=\"no_external_map_app\">Δε βρέθηκε εφαρμογή για χάρτες!</string>\n    <string name=\"click_to_show_full\">Σε πόσους ακριβώς αρέσει</string>\n    <string name=\"no_profile_pic_found\">Δε βρέθηκε εικόνα προφίλ!</string>\n    <string name=\"swipe_up_confirmation\">Είστε βέβαιος για το άνοιγμα αυτού του συνδέσμου;</string>\n    <string name=\"sending\">Αποστολή…</string>\n    <string name=\"share_via_dm\">Κοινοποίηση μέσω μηνύματος</string>\n    <string name=\"share_link\">Κοινοποίηση συνδέσμου…</string>\n    <string name=\"slide_to_cancel\">Σύρετε για ακύρωση</string>\n    <string name=\"disable_screen_transitions\">Απενεργοποίηση μεταβάσεων οθόνης</string>\n    <string name=\"invalid_format\">Μη έγκυρη μορφή</string>\n    <string name=\"no_directory_picker_activity\">Το Barinsta δεν μπορεί να εκκινήσει τον διαχειριστή αρχείων του Android. Παρακαλώ βεβαιωθείτε ότι είναι εγκατεστημένο και ενεργοποιημένο στη συσκευή σας.</string>\n    <string name=\"story_stickers\">Αυτοκόλλητα</string>\n    <string name=\"story_list\">Λίστα ιστοριών</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-es/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Predeterminado del sistema</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Auto / Seguir al sistema</item>\n        <item>Auto / Seguir a la batería</item>\n        <item>Oscuro</item>\n        <item>Claro</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Predeterminado de Instagram (no leídos, después leídos)</item>\n        <item>Del más reciente al más antiguo</item>\n        <item>Del más antiguo al más reciente</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Ninguno</item>\n        <item>\\@</item>\n        <item>en</item>\n        <item>en</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>segs</item>\n        <item>mins</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Acerca de</string>\n    <string name=\"action_dms\">Mensajes Directos</string>\n    <string name=\"action_settings\">Ajustes</string>\n    <string name=\"action_download\">Descargar</string>\n    <string name=\"action_search\">Buscar usuario…</string>\n    <string name=\"action_compare\">Comparar</string>\n    <string name=\"clipboard_error\">Error al copiar texto</string>\n    <string name=\"clipboard_copied\">¡Copiado al portapapeles!</string>\n    <string name=\"report\">Reportar</string>\n    <string name=\"set_password\">Proteger archivo con contraseña</string>\n    <string name=\"password_no_max\">Contraseña</string>\n    <string name=\"ok\">Aceptar</string>\n    <string name=\"yes\">Sí</string>\n    <string name=\"cancel\">Cancelar</string>\n    <string name=\"no\">No</string>\n    <string name=\"confirm\">Confirmar</string>\n    <string name=\"title_favorites\">Favoritos</string>\n    <string name=\"title_discover\">Explorar</string>\n    <string name=\"title_comments\">Comentarios</string>\n    <string name=\"title_replies\">Respuestas</string>\n    <string name=\"title_notifications\">Actividad</string>\n    <string name=\"update_check\">Buscar actualizaciones al inicio</string>\n    <string name=\"flag_secure\">Bloquea capturas de pantalla &amp; vista previa de aplicaciones</string>\n    <string name=\"download_user_folder\">Usar subcarpetas con el nombre de usuario</string>\n    <string name=\"download_prepend_username\">Añadir nombre de usuario al inicio del nombre del archivo</string>\n    <string name=\"mark_as_seen_setting\">Marcar historias como vistas después de verlas</string>\n    <string name=\"mark_as_seen_setting_summary\">El autor de la historia sabrá que lo has visto</string>\n    <string name=\"hide_muted_reels_setting\">Ocultar historias silenciadas del feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Marcar Mensaje Directo como visto después de verlo</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Otros miembros sabrán que lo has visto</string>\n    <string name=\"autoplay_stories_setting\">Autorreproducir vídeohistorias</string>\n    <string name=\"story_list_setting\">Mostrar lista de historias por defecto</string>\n    <string name=\"story_list_setting_summary\">Para ver historias</string>\n    <string name=\"activity_setting\">Activar notificaciones de actividad</string>\n    <string name=\"story_sort_setting\">Orden de las historias</string>\n    <string name=\"error_loading_profile\">¡Error al cargar el perfil! ¿Es el nombre de usuario válido? Si es así, puede que su conexión esté baneada en el servidor.</string>\n    <string name=\"error_loading_hashtag\">¡Error cargando el hashtag! ¿Es válido el nombre?</string>\n    <string name=\"error_loading_location\">¡Error cargando la ubicación! ¿Es válida la URL?</string>\n    <string name=\"error_creating_folders\">Error creando carpeta(s) de descarga.</string>\n    <string name=\"select_folder\">Seleccionar carpeta</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Idioma</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPublicación</item>\n        <item quantity=\"other\">%s\\nPublicaciones</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Publicación</item>\n        <item quantity=\"other\">%s Publicaciones</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSeguidor</item>\n        <item quantity=\"other\">%s\\nSeguidores</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nSiguiendo</string>\n    <string name=\"post_viewer_autoplay_video\">Autorreproducir vídeos</string>\n    <string name=\"post_viewer_background_play\">Continuar vídeos en segundo plano</string>\n    <string name=\"post_viewer_background_play_summary\">No pausar vídeos cuando la aplicación está fuera de enfoque</string>\n    <string name=\"post_viewer_muted_autoplay\">Siempre silenciar vídeos</string>\n    <string name=\"post_viewer_show_captions\">Mostrar siempre subtítulos del post</string>\n    <string name=\"post_viewer_download_dialog_title\">Seleccionar qué descargar</string>\n    <string name=\"post_viewer_download_current\">Actual</string>\n    <string name=\"post_viewer_download_album\">Todo el álbum</string>\n    <string name=\"show_stories\">Mostrar historias</string>\n    <string name=\"no_more_stories\">¡No hay más historias!</string>\n    <string name=\"view_post\">Ver publicación</string>\n    <string name=\"story_poll\">Encuesta</string>\n    <string name=\"answered_story\">¡Has respondido con éxito!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d respuesta promediando un %s</item>\n        <item quantity=\"other\">%d respuestas promediando un %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Tu respuesta: %s</string>\n    <string name=\"reply_story\">Responder a la historia</string>\n    <string name=\"reply_hint\">Responder…</string>\n    <string name=\"story_quiz\">Cuestionario</string>\n    <string name=\"story_slider\">Deslizador</string>\n    <string name=\"story_quizzed\">¡Ya has respondido!</string>\n    <string name=\"story_mentions\">Menciones</string>\n    <string name=\"story_question\">Pregunta</string>\n    <string name=\"priv_acc\">Esta cuenta es privada</string>\n    <string name=\"priv_acc_confirm\">¡No podrás acceder a sus publicaciones después de dejar de seguirle! ¿Estás seguro?</string>\n    <string name=\"are_you_sure\">¿Estás seguro/a?</string>\n    <string name=\"no_acc\">¡Puedes iniciar sesión a través de Más -&gt; Cuenta en la esquina inferior derecha o puedes ver cuentas públicas sin iniciar sesión!</string>\n    <string name=\"empty_acc\">Esta cuenta no tiene publicaciones</string>\n    <string name=\"empty_list\">¡No existen tales publicaciones!</string>\n    <string name=\"login\">Iniciar sesión</string>\n    <string name=\"logout\">Cerrar sesión</string>\n    <string name=\"logout_summary\">Navegar Instagram anónimamente</string>\n    <string name=\"remove_all_acc\">Eliminar todas las cuentas</string>\n    <string name=\"remove_all_acc_warning\">¡Esto eliminará todas las cuentas añadidas de la aplicación!\\nPara eliminar solo una cuenta, mantén pulsada la cuenta desde la ventana de cambiador de cuenta.\\n¿Quieres continuar?</string>\n    <string name=\"time_settings\">Formato de fecha</string>\n    <string name=\"saved_create_collection\">Crear nueva colección</string>\n    <string name=\"edit_collection\">Editar nombre de colección</string>\n    <string name=\"delete_collection\">Eliminar colección</string>\n    <string name=\"delete_collection_note\">Todas las publicaciones contenidas en la colección eliminada permanecerán en otras colecciones.</string>\n    <string name=\"add_to_collection\">Añadir a la colección…</string>\n    <string name=\"remove_from_collection\">Eliminar de la colección</string>\n    <string name=\"liked\">Gustado</string>\n    <string name=\"saved\">Guardado</string>\n    <string name=\"tagged\">Etiquetado</string>\n    <string name=\"dm_person\">Mensaje</string>\n    <string name=\"follow\">Seguir</string>\n    <string name=\"unfollow\">Dejar de seguir</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Agregar a favoritos</string>\n    <string name=\"block\">Bloquear</string>\n    <string name=\"unblock\">Desbloquear</string>\n    <string name=\"restrict\">Restringir</string>\n    <string name=\"unrestrict\">Desrestringir</string>\n    <string name=\"mute_stories\">Silenciar historias</string>\n    <string name=\"mute_posts\">Silenciar publicaciones</string>\n    <string name=\"unmute_stories\">Quitar silencio a historias</string>\n    <string name=\"unmute_posts\">Quitar silencio a publicaciones</string>\n    <string name=\"remove_follower\">Quitar seguidor</string>\n    <string name=\"bio_copy\">Copiar biografía</string>\n    <string name=\"bio_translate\">Traducir biografía</string>\n    <string name=\"status_mutual\">Mutuo</string>\n    <string name=\"status_following\">Siguiendo</string>\n    <string name=\"status_follower\">Seguidor</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Cuentas</string>\n    <string name=\"dialog_export_settings\">Ajustes</string>\n    <string name=\"dialog_export_favorites\">Favoritos</string>\n    <string name=\"dialog_import_success\">¡Importado con éxito!</string>\n    <string name=\"dialog_import_failed\">¡Falló la importación!</string>\n    <string name=\"dialog_export_success\">¡Exportado con éxito!</string>\n    <string name=\"dialog_export_failed\">¡Falló la exportación!</string>\n    <string name=\"refresh\">Actualizar</string>\n    <string name=\"get_cookies\">Obtener cookies</string>\n    <string name=\"time_settings_title_custom\">Usar formato personalizado</string>\n    <string name=\"time_settings_title_separator\">Separador</string>\n    <string name=\"time_settings_title_time_format\">Formato de tiempo</string>\n    <string name=\"time_settings_title_date_format\">Formato de fecha</string>\n    <string name=\"time_settings_title_preview\">Previsualizar</string>\n    <string name=\"time_settings_swap_time\">Intercambiar posiciones de hora y fecha</string>\n    <string name=\"quick_access_cannot_delete_curr\">No se puede borrar la cuenta actualmente en uso</string>\n    <string name=\"quick_access_confirm_delete\">¿Estás seguro de que quieres borrar \\'%s\\'?</string>\n    <string name=\"open_profile\">Abrir perfil</string>\n    <string name=\"view_story\">Ver historia</string>\n    <string name=\"view_pfp\">Ver foto de perfil</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Tipo de mensaje no soportado</string>\n    <string name=\"dms_inbox_unsend\">Cancelar envío de mensaje</string>\n    <string name=\"dms_inbox_giphy\">Ver en GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s ha compartido una publicación de @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s compartió una imagen</string>\n    <string name=\"dms_inbox_shared_video\">%s compartió un vídeo</string>\n    <string name=\"dms_inbox_shared_message\">%s envió un mensaje</string>\n    <string name=\"dms_inbox_shared_gif\">%s compartió un gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s compartió un sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s compartió un perfil: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s compartió una ubicación: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s compartió una historia destacada de @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s compartió una historia de @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s envió un mensaje de voz</string>\n    <string name=\"dms_inbox_shared_clip\">%s compartió un clip de @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s compartió un vídeo de IGTV de @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Respondiste a su historia: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s respondió a tu historia: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Reaccionaste a su historia: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reaccionó a tu historia: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Mencionaste a @%s en tu historia</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s te mencionó en su historia</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Tipo de medio desconocido</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">¡Archivo multimedia caducado!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Entregado</string>\n    <string name=\"dms_inbox_raven_media_sent\">Enviado</string>\n    <string name=\"dms_inbox_raven_media_opened\">Abierto</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Reproducido de nuevo</string>\n    <string name=\"dms_inbox_raven_media_sending\">Enviando…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Bloqueado</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Sugerido</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Captura de pantalla realizada</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">No se puede entregar</string>\n    <string name=\"dms_thread_message_hint\">Mensaje…</string>\n    <string name=\"dms_thread_audio_hint\">Pulsa y mantén presionado para grabar audio</string>\n    <string name=\"dms_thread_updating\">Actualizando…</string>\n    <string name=\"dms_action_leave\">Abandonar chat</string>\n    <string name=\"dms_action_leave_question\">¿Salir de este chat?</string>\n    <string name=\"dms_action_kick\">Expulsar</string>\n    <string name=\"dms_left_users\">Usuarios que han abandonado</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Usuario no válido</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram no permite subir vídeos de más de 60 segundos para MD.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram no permite subir audios de más de 60 segundos.</string>\n    <string name=\"direct_download_loading\">Obteniendo publicación(es)</string>\n    <string name=\"downloader_complete\">Descarga completada</string>\n    <string name=\"downloader_preparing\">Preparando para descargar…</string>\n    <string name=\"downloader_downloading_post\">Descargando publicación…</string>\n    <string name=\"downloader_downloading_media\">Descargando multimedia</string>\n    <string name=\"downloader_unknown_error\">¡¡¡Ha ocurrido un error desconocido!!!</string>\n    <string name=\"downloader_error_creating_folder\">¡Error al crear carpeta!</string>\n    <string name=\"downloader_error_download_file\">Error al descargar archivo</string>\n    <string name=\"comment_viewer_translate_comment\">Traducir comentario</string>\n    <string name=\"comment_viewer_delete_comment\">Eliminar comentario</string>\n    <string name=\"followers_type_followers\">Seguidores</string>\n    <string name=\"followers_type_following\">Siguiendo</string>\n    <string name=\"followers_compare\">Comparando seguidores &amp; seguidos</string>\n    <string name=\"followers_both_following\">Ambos se siguen mutuamente</string>\n    <string name=\"followers_not_following\">No sigue a %s</string>\n    <string name=\"followers_not_follower\">%s no sigue a</string>\n    <string name=\"login_error_loading_cookies\">Error al cargar cookies</string>\n    <string name=\"comment_hint\">Escribir un nuevo comentario…</string>\n    <string name=\"liked_notif\">Le ha gustado tu publicación</string>\n    <string name=\"comment_notif\">Ha comentado en tu publicación:</string>\n    <string name=\"follow_notif\">Ha empezado a seguirte</string>\n    <string name=\"tagged_notif\">Te ha etiquetado en una publicación</string>\n    <string name=\"request_notif\">Solicita seguirte</string>\n    <string name=\"request_approve\">Aprobar solicitud</string>\n    <string name=\"request_reject\">Rechazar solicitud</string>\n    <string name=\"share_public_post\">Compartir esta publicación pública a…</string>\n    <string name=\"share_private_post\">¡Esta es una publicación privada! Comparte con quienes puedan verla.</string>\n    <string name=\"discover_empty\">Esta categoría está de alguna manera vacía…</string>\n    <string name=\"update_available\">¡Hay una actualización disponible! (%s)</string>\n    <string name=\"updated\">¡Gracias por actualizar Barinsta!</string>\n    <string name=\"crash_title\">La aplicación se ha bloqueado</string>\n    <string name=\"crash_descr\">Vaya.. La aplicación dejó de funcionar, pero no te preocupes, puedes enviar un reporte de error al desarrollador para ayudarle a arreglar el problema. (:</string>\n    <string name=\"action_notif\">Actividad</string>\n    <string name=\"action_archive\">Archivo de historias</string>\n    <string name=\"action_ayml\">Usuarios sugeridos</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">Tienes %d notificación</item>\n        <item quantity=\"other\">Tienes %d notificaciones</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d sigue</string>\n    <string name=\"activity_count_comments\">%d comentarios</string>\n    <string name=\"activity_count_commentlikes\">%d me gustas en comentarios</string>\n    <string name=\"activity_count_usertags\">%d etiquetas</string>\n    <string name=\"activity_count_likes\">%d me gustas</string>\n    <string name=\"activity_count_poy\">%d fotos de ti</string>\n    <string name=\"activity_count_requests\">%d solicitudes de seguimiento</string>\n    <string name=\"activity_notloggedin\">¡¿Has cerrado sesión antes de hacer clic en esta notificación?!</string>\n    <string name=\"feed\">Muro</string>\n    <string name=\"profile\">Perfil</string>\n    <string name=\"more\">Más</string>\n    <string name=\"title_dm\">MD</string>\n    <string name=\"number_selected\">%d seleccionados</string>\n    <string name=\"logout_success\">¡Sesión cerrada exitosamente!</string>\n    <string name=\"dm_thread_info\">Información</string>\n    <string name=\"mark_as_seen\">Marcar como visto</string>\n    <string name=\"version\">Versión</string>\n    <string name=\"pref_start_screen\">Pantalla de inicio</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Mostrar teclado al buscar</string>\n    <string name=\"pref_category_general\">General</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Descargas</string>\n    <string name=\"pref_category_locale\">Configuración regional</string>\n    <string name=\"account\">Cuenta</string>\n    <string name=\"account_hint\">¿El inicio de sesión actual no funciona? Simplemente añade la cuenta de nuevo.</string>\n    <string name=\"add_account\">Añadir cuenta</string>\n    <string name=\"about_category_license\">Licencia (sólo en inglés)</string>\n    <string name=\"about_documentation\">Visita nuestra página web</string>\n    <string name=\"about_documentation_summary\">¡Obtenga soporte, discuta, conozca a otros y diviértase!</string>\n    <string name=\"about_repository\">Vea nuestro código fuente en GitHub</string>\n    <string name=\"about_repository_summary\">¡Audite, añada a favoritos, reporte errores, contribuya y diviértase (de nuevo)!</string>\n    <string name=\"about_feedback\">Enviar opinión por correo electrónico</string>\n    <string name=\"about_category_3pt\">Créditos de terceros</string>\n    <string name=\"reminder\">Recordatorio</string>\n    <string name=\"reminder_summary\">Por favor, utilice esta aplicación de forma responsable. Las imágenes descargadas sólo deben utilizarse para propósitos permitidos por las leyes aplicables.</string>\n    <string name=\"light_white_theme\">Blanco</string>\n    <string name=\"dark_black_theme\">Negro</string>\n    <string name=\"light_theme_settings\">Tema claro</string>\n    <string name=\"dark_theme_settings\">Tema oscuro</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material oscuro</string>\n    <string name=\"added_to_favs\">¡Añadido a favoritos!</string>\n    <string name=\"add_to_favorites\">Añadir a favoritos</string>\n    <string name=\"accounts\">Cuentas</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Ubicaciones</string>\n    <string name=\"unknown\">Desconocido</string>\n    <string name=\"removed_from_favs\">¡Eliminado de favoritos!</string>\n    <string name=\"backup_and_restore\">Respaldar &amp; restaurar</string>\n    <string name=\"auto_backup\">Respaldo automático</string>\n    <string name=\"auto_backup_summary\">A partir de Android 6, la función Respaldo automático de Android subirá todos los ajustes de la aplicación, los datos de inicio de sesión de la cuenta y favoritos en tu Google Drive, que se puede restaurar reinstalando la aplicación después de la desinstalación.</string>\n    <string name=\"auto_backup_warning\">Esta preferencia no tiene efecto si Google Play Services no está presente, o si el respaldo automático está deshabilitado de la configuración de su dispositivo. Deshabilitar aquí no borra los respaldos existentes.</string>\n    <string name=\"auto_backup_setting\">Habilitar respaldos automáticos</string>\n    <string name=\"manual_backup\">Respaldo manual</string>\n    <string name=\"backup_summary\">Respalda los ajustes de Barinsta, datos de inicios de sesión, y/o favoritos a un archivo de texto plano o a un archivo cifrado para su posterior restauración.</string>\n    <string name=\"backup_warning\">Si está respaldando datos de inicios de sesión, trate el archivo como confidencial y ¡manténgalo a buen recaudo!</string>\n    <string name=\"create_backup\">Crear un nuevo archivo del respaldo</string>\n    <string name=\"restore_backup\">Restaurar desde un archivo de respaldo existente</string>\n    <string name=\"file_chosen_label\">Archivo:</string>\n    <string name=\"enter_password\">Introduzca contraseña</string>\n    <string name=\"select_backup_file\">Seleccione un archivo de respaldo (.zaai/.backup)</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"save\">Guardar</string>\n    <string name=\"caption\">Leyenda</string>\n    <string name=\"edit_caption\">Editar leyenda</string>\n    <string name=\"translate_caption\">Traducir leyenda</string>\n    <string name=\"player_timeline_desc\">Línea de tiempo del reproductor de vídeo</string>\n    <string name=\"liking\">Gustando…</string>\n    <string name=\"like_unsuccessful\">Me gusta fallido</string>\n    <string name=\"unlike_unsuccessful\">Quitando me gusta fallido</string>\n    <string name=\"unliking\">Quitando me gusta…</string>\n    <string name=\"controls\">Controles</string>\n    <string name=\"saving\">Guardando…</string>\n    <string name=\"removing\">Eliminando…</string>\n    <string name=\"save_unsuccessful\">Guardar fallido</string>\n    <string name=\"save_remove_unsuccessful\">Eliminación fallida</string>\n    <string name=\"downloading\">Descargando…</string>\n    <string name=\"downloader_downloading_child\">Descargar elemento %1$d de %2$d</string>\n    <string name=\"delete\">Borrar</string>\n    <string name=\"comment\">Comentario</string>\n    <string name=\"layout\">Disposición</string>\n    <string name=\"feed_stories\">Muro de historias</string>\n    <string name=\"opening_post\">Abriendo publicación…</string>\n    <string name=\"share\">Compartir</string>\n    <string name=\"layout_style\">Estilo de disposición</string>\n    <string name=\"column_count\">Número de columnas</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Mostrar nombres</string>\n    <string name=\"show_avatars\">Mostrar avatares</string>\n    <string name=\"avatar_size\">Tamaño de avatar</string>\n    <string name=\"corners\">Esquinas</string>\n    <string name=\"show_grid_gap\">Mostrar espaciado de cuadrícula</string>\n    <string name=\"post_not_found\">¡Publicación no encontrada!</string>\n    <string name=\"no_external_app_url\">No se encontraron aplicaciones para abrir URLs</string>\n    <string name=\"gallery\">Galería</string>\n    <string name=\"camera\">Cámara</string>\n    <string name=\"all_photos\">Todas las fotos</string>\n    <string name=\"all_media\">Toda la multimedia</string>\n    <string name=\"all_videos\">Todos los vídeos</string>\n    <string name=\"brightness\">Brillo</string>\n    <string name=\"contrast\">Contraste</string>\n    <string name=\"vibrance\">Intensidad</string>\n    <string name=\"saturation\">Saturación</string>\n    <string name=\"sharpen\">Dar nitidez</string>\n    <string name=\"exposure\">Exposición</string>\n    <string name=\"center\">Centrar</string>\n    <string name=\"color\">Color</string>\n    <string name=\"start\">Inicio</string>\n    <string name=\"end\">Fin</string>\n    <string name=\"bilateral_blur\">Desenfoque bilateral</string>\n    <string name=\"vignette\">Viñeta</string>\n    <string name=\"box_blur\">Desenfoque de caja</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendón</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Reiniciar</string>\n    <string name=\"crop\">Recortar</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d visualización</item>\n        <item quantity=\"other\">%d visualizaciones</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s historia</item>\n        <item quantity=\"other\">%s historias</item>\n    </plurals>\n    <string name=\"details\">Detalles</string>\n    <string name=\"title\">Título</string>\n    <string name=\"members\">Miembros</string>\n    <string name=\"admin\">Administrador</string>\n    <string name=\"inviter\">Invitado</string>\n    <string name=\"mute_messages\">Silenciar mensajes</string>\n    <string name=\"mute_mentions\">Silenciar menciones</string>\n    <string name=\"add_members\">Añadir miembros</string>\n    <string name=\"search\">Buscar</string>\n    <string name=\"done\">Hecho</string>\n    <string name=\"dms_action_make_admin\">Hacer administrador</string>\n    <string name=\"dms_action_remove_admin\">Retirar administrador</string>\n    <string name=\"edit_unsuccessful\">La edición no tuvo éxito</string>\n    <string name=\"message\">Mensaje</string>\n    <string name=\"tap_to_remove\">Toca para eliminar</string>\n    <string name=\"forward\">Reenviar</string>\n    <string name=\"forward_outgoing\">Has reenviado un mensaje</string>\n    <string name=\"forward_incoming\">Mensaje reenviado</string>\n    <string name=\"add\">Añadir</string>\n    <string name=\"send\">Enviar</string>\n    <string name=\"replying_to_yourself\">Respondiendo a ti mismo</string>\n    <string name=\"replying_to_user\">Respondiendo a %s</string>\n    <string name=\"replied_to_yourself\">Te respondiste a ti mismo</string>\n    <string name=\"replied_you\">Respondiste</string>\n    <string name=\"replied_you_group\">Respondiste a %s</string>\n    <string name=\"replied_group\">Respondido a %s</string>\n    <string name=\"replied_to_you\">Te respondió</string>\n    <string name=\"replied_to_themself\">Respondido a ellos mismos</string>\n    <string name=\"reacted_story_outgoing\">Reaccionaste a su historia</string>\n    <string name=\"reacted_story_incoming\">Reaccionó a tu historia</string>\n    <string name=\"mentioned_story_outgoing\">Los mencionaste en tu historia</string>\n    <string name=\"mentioned_story_incoming\">Te mencionaron en su historia</string>\n    <string name=\"replied_story_outgoing\">Respondiste a su historia</string>\n    <string name=\"replied_story_incoming\">Respondieron a tu historia</string>\n    <string name=\"raven_image_expired\">La imagen ha expirado</string>\n    <string name=\"raven_image_info\">La imagen expirará cuando se vea</string>\n    <string name=\"raven_video_expired\">El vídeo ha expirado</string>\n    <string name=\"raven_video_info\">El vídeo expirará cuando se vea</string>\n    <string name=\"raven_msg_expired\">El mensaje ha expirado</string>\n    <string name=\"raven_msg_info\">El mensaje expirará cuando se vea</string>\n    <string name=\"story_share\">Historia de @%s</string>\n    <string name=\"story_share_highlight\">Resaltado de historia de %s</string>\n    <string name=\"photo\">Fotografía</string>\n    <string name=\"video\">Vídeo</string>\n    <string name=\"voice_message\">Mensaje de voz</string>\n    <string name=\"post\">Publicación</string>\n    <string name=\"approval_required_for_new_members\">Se requiere aprobación para unirse</string>\n    <string name=\"requests\">Solicitudes</string>\n    <string name=\"admins_only\">Sólo administradores</string>\n    <string name=\"added_by\">Añadido por %s</string>\n    <string name=\"admin_approval_required\">Aprobación del administrador requerida</string>\n    <string name=\"admin_approval_required_description\">Se requerirá una aprobación del administrador para añadir nuevos miembros al grupo</string>\n    <string name=\"dms_action_end\">Terminar el chat</string>\n    <string name=\"dms_action_end_question\">¿Terminar el chat?</string>\n    <string name=\"dms_action_end_description\">Todos los miembros serán eliminados del grupo. Aún podrán ver el historial del chat.</string>\n    <string name=\"pending_requests\">Solicitudes pendientes</string>\n    <string name=\"accept_request_from_user\">¿Aceptar solicitud de %1s (%2s)?</string>\n    <string name=\"decline\">Rechazar</string>\n    <string name=\"accept\">Aceptar</string>\n    <string name=\"you\">Tú</string>\n    <string name=\"no_pending_requests\">No hay solicitudes pendientes</string>\n    <string name=\"checking_for_new_messages\">Comprobando mensajes nuevos</string>\n    <string name=\"pref_category_stories\">Historias</string>\n    <string name=\"pref_category_dm\">MD</string>\n    <string name=\"pref_category_notifications\">Notificaciones</string>\n    <string name=\"pref_category_post\">Publicación</string>\n    <string name=\"enable_dm_notifications\">Habilitar notificaciones de MDs</string>\n    <string name=\"enable_dm_auto_refesh\">Autorefrescar mensajes</string>\n    <string name=\"auto_refresh_every\">Autorefrescar cada</string>\n    <string name=\"secs\">segs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Buscar GIPHY</string>\n    <string name=\"generic_null_response\">¡La respuesta es nula!</string>\n    <string name=\"generic_not_ok_response\">¡El estado de la respuesta no es correcto!</string>\n    <string name=\"generic_failed_request\">¡Solicitud fallida!</string>\n    <string name=\"hint_keyword\">Palabra clave</string>\n    <string name=\"toggle_keyword_filter\">Activar filtro de palabras clave</string>\n    <string name=\"edit_keyword_filter\">Editar filtros de palabras clave</string>\n    <string name=\"added_keywords\">Palabra clave añadida: %s a la lista de filtros</string>\n    <string name=\"removed_keywords\">Se eliminó la palabra clave: %s de la lista de filtros</string>\n    <string name=\"marked_as_seen\">Marcado como visto</string>\n    <string name=\"delete_unsuccessful\">Eliminación fallida</string>\n    <string name=\"throttle_error\">Restringido por Instagram por hacer demasiadas solicitudes de API. Espera un tiempo antes de reintentar.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">Esta cuenta ha sido desconectada.</string>\n    <string name=\"login_required\">¡Inicio de sesión requerido!</string>\n    <string name=\"inactive_user\">¡Usuario inactivo!</string>\n    <string name=\"crash_report_subject\">Informe de fallos de Barinsta</string>\n    <string name=\"crash_report_title\">Seleccione una aplicación de correo electrónico para enviar registros de errores</string>\n    <string name=\"not_found\">¡No encontrado!</string>\n    <string name=\"rate_limit\">Tu IP ha sido limitada por Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Más información&lt;/a&gt;</string>\n    <string name=\"skip_update\">Omitir esta actualización</string>\n    <string name=\"on_latest_version\">Ya tienes la última versión</string>\n    <string name=\"tab_order\">Orden de pantalla</string>\n    <string name=\"other_tabs\">Otras pestañas</string>\n    <string name=\"tab_order_start_next_launch\">El orden de la pestaña se reflejará en el próximo lanzamiento</string>\n    <string name=\"dm_remove_warning\">Si se guarda, todas las funcionalidades relacionadas con MDs se desactivarán en el próximo lanzamiento</string>\n    <string name=\"copy_caption\">Copiar título</string>\n    <string name=\"copy_reply\">Copiar respuesta</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"backup\">Respaldar</string>\n    <string name=\"dir_select_default_message\">Seleccione una carpeta donde Barinsta pueda almacenar descargas y archivos temporales.\\n\\nPuede cambiar esto más tarde en Más &gt; Ajustes &gt; Descargas.</string>\n    <string name=\"dir_select_reselect_message\">Android ha cambiado la forma en que las aplicaciones pueden acceder a archivos y directorios en el almacenamiento. Actualmente Barinsta no tiene permiso para acceder a la siguiente carpeta:</string>\n    <string name=\"dir_select_permission_revoked_message\">Los permisos para la carpeta seleccionada anteriormente fueron revocados por el sistema:</string>\n    <string name=\"dir_select_folder_not_exist\">La carpeta seleccionada anteriormente no existe ahora:</string>\n    <string name=\"dir_select_message2\">Vuelva a seleccionar el directorio o seleccione un nuevo directorio haciendo clic en el botón de abajo.</string>\n    <string name=\"select_a_folder\">¡Ninguna carpeta seleccionada!</string>\n    <string name=\"dir_select_no_download_folder\">Por favor, elija un directorio de su almacenamiento, no una categoría en la barra lateral.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">¡Éxito! Por favor espere. Iniciando aplicación…</string>\n    <string name=\"barinsta_folder\">Carpeta Barinsta</string>\n    <string name=\"top\">Arriba</string>\n    <string name=\"recent\">Reciente</string>\n    <string name=\"clear\">Eliminar</string>\n    <string name=\"no_external_map_app\">¡No se encontró una aplicación de mapa!</string>\n    <string name=\"click_to_show_full\">Clic para ver el recuento completo de me gustas</string>\n    <string name=\"no_profile_pic_found\">¡No se encontró foto de perfil!</string>\n    <string name=\"swipe_up_confirmation\">¿Está seguro de querer abrir este enlace?</string>\n    <string name=\"sending\">Enviando…</string>\n    <string name=\"share_via_dm\">Compartir por MD</string>\n    <string name=\"share_link\">Compartir enlace…</string>\n    <string name=\"slide_to_cancel\">Desliza para cancelar</string>\n    <string name=\"disable_screen_transitions\">Desactivar transiciones de pantalla</string>\n    <string name=\"invalid_format\">Formato no válido</string>\n    <string name=\"no_directory_picker_activity\">Barinsta no puede iniciar el gestor de archivos de Android. Por favor, asegúrese de que está instalado y habilitado en su dispositivo.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Lista de historias</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-eu/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Sistemaren lehenetsia</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automatikoa / Jarraitu sistema</item>\n        <item>Automatikoa / Jarraitu bateria</item>\n        <item>Iluna</item>\n        <item>Argia</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagrameko lehenetsia (Ez irakurrita, gero irakurrita)</item>\n        <item>Berrienetik zaharrenera</item>\n        <item>Zaharrenetik berrienera</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Bat ere ez</item>\n        <item>\\@</item>\n        <item>-</item>\n        <item>,</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>s</item>\n        <item>m</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-eu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Honi buruz</string>\n    <string name=\"action_dms\">Mezu zuzenak</string>\n    <string name=\"action_settings\">Ezarpenak</string>\n    <string name=\"action_download\">Deskargatu</string>\n    <string name=\"action_search\">Bilatu erabiltzaile-izena…</string>\n    <string name=\"action_compare\">Alderatu</string>\n    <string name=\"clipboard_error\">Errorea testua kopiatzean</string>\n    <string name=\"clipboard_copied\">Arbelera kopiatu da!</string>\n    <string name=\"report\">Salatu</string>\n    <string name=\"set_password\">Babestu fitxategia pasahitzarekin</string>\n    <string name=\"password_no_max\">Pasahitza</string>\n    <string name=\"ok\">Ados</string>\n    <string name=\"yes\">Bai</string>\n    <string name=\"cancel\">Utzi</string>\n    <string name=\"no\">Ez</string>\n    <string name=\"confirm\">Baieztatu</string>\n    <string name=\"title_favorites\">Gogokoak</string>\n    <string name=\"title_discover\">Aurkitu</string>\n    <string name=\"title_comments\">Iruzkinak</string>\n    <string name=\"title_replies\">Erantzunak</string>\n    <string name=\"title_notifications\">Jarduera</string>\n    <string name=\"update_check\">Bilatu eguneratzeak abioan</string>\n    <string name=\"flag_secure\">Blokeatu pantaila-argazkiak eta aplikazioaren aurrebista</string>\n    <string name=\"download_user_folder\">Deskargatu bidalketak erabiltzaile-izena duten karpetetara</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Markatu istorioak ikusita gisa ikusi ondoren</string>\n    <string name=\"mark_as_seen_setting_summary\">Istorioaren egileak ikusi duzula jakingo du</string>\n    <string name=\"hide_muted_reels_setting\">Hide muted stories from feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Markatu MZ ikusita gisa ikusi ondoren</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Beste kideek ikusi duzula jakingo dute</string>\n    <string name=\"autoplay_stories_setting\">Erreproduzitu automatikoki bideoa diren istorioak</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Gaitu jarduera-jakinarazpenak</string>\n    <string name=\"story_sort_setting\">Istorioen jarioaren sailkapena</string>\n    <string name=\"error_loading_profile\">Error loading profile! Is the username valid? If so, you may be ratelimited.</string>\n    <string name=\"error_loading_hashtag\">Error loading hashtag! Is the name valid?</string>\n    <string name=\"error_loading_location\">Error loading location! Is the URL valid?</string>\n    <string name=\"error_creating_folders\">Errorea deskargen karpeta(k) sortzen.</string>\n    <string name=\"select_folder\">Hautatu karpeta</string>\n    <string name=\"theme_settings\">Gaia</string>\n    <string name=\"select_language\">Hizkuntza</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">Bidalketa\\n%s</item>\n        <item quantity=\"other\">%s\\nBidalketa</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">Bidalketa %s</item>\n        <item quantity=\"other\">%s Bidalketa</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nJarraitzaile</item>\n        <item quantity=\"other\">%s\\nJarraitzaile</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nJarraituak</string>\n    <string name=\"post_viewer_autoplay_video\">Erreproduzitu bideoak automatikoki</string>\n    <string name=\"post_viewer_background_play\">Mantendu bideoak bigarren planoan</string>\n    <string name=\"post_viewer_background_play_summary\">Do not pause videos when the app is out of focus</string>\n    <string name=\"post_viewer_muted_autoplay\">Mututu bideoak beti</string>\n    <string name=\"post_viewer_show_captions\">Erakutsi argazki-oina beti</string>\n    <string name=\"post_viewer_download_dialog_title\">Hautatu zer deskargatu</string>\n    <string name=\"post_viewer_download_current\">Unekoa</string>\n    <string name=\"post_viewer_download_album\">Album osoa</string>\n    <string name=\"show_stories\">Erakutsi istorioak</string>\n    <string name=\"no_more_stories\">Istorio gehiagorik ez</string>\n    <string name=\"view_post\">Ikusi bidalketa</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Erantzuna ongi bidali da!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Zure erantzuna: %s</string>\n    <string name=\"reply_story\">Erantzun istorioa</string>\n    <string name=\"reply_hint\">Erantzun…</string>\n    <string name=\"story_quiz\">Lehiaketa</string>\n    <string name=\"story_slider\">Irristatze-barra</string>\n    <string name=\"story_quizzed\">Dagoeneko erantzun duzu!</string>\n    <string name=\"story_mentions\">Aipamenak</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Kontu hau pribatua da</string>\n    <string name=\"priv_acc_confirm\">Ezingo dituzu bidalketak ikusi jarraitzeari utzi ondoren. Ziur zaude?</string>\n    <string name=\"are_you_sure\">Ziur zaude?</string>\n    <string name=\"no_acc\">You can log in via More -&gt; Account on the bottom-right corner or you can view public accounts without login!</string>\n    <string name=\"empty_acc\">Kontu honek bidalketarik ez du</string>\n    <string name=\"empty_list\">Bidalketarik ez!</string>\n    <string name=\"login\">Hasi saioa</string>\n    <string name=\"logout\">Amaitu saioa</string>\n    <string name=\"logout_summary\">Nabigatu Instagramen anonimoki</string>\n    <string name=\"remove_all_acc\">Kendu kontu guztiak</string>\n    <string name=\"remove_all_acc_warning\">This will remove all added accounts from the app!\\nTo remove just one account, long tap the account from the account switcher dialog.\\nDo you want to continue?</string>\n    <string name=\"time_settings\">Data-formatua</string>\n    <string name=\"saved_create_collection\">Bilduma berria sortu</string>\n    <string name=\"edit_collection\">Editatu bildumaren izena</string>\n    <string name=\"delete_collection\">Ezabatu bilduma</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Kendu bildumatik</string>\n    <string name=\"liked\">Atsegiteak</string>\n    <string name=\"saved\">Gordeta</string>\n    <string name=\"tagged\">Etiketatuta</string>\n    <string name=\"dm_person\">Mezua</string>\n    <string name=\"follow\">Jarraitu</string>\n    <string name=\"unfollow\">Jarraitzeari utzi</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Gogokoa</string>\n    <string name=\"block\">Blokeatu</string>\n    <string name=\"unblock\">Desblokeatu</string>\n    <string name=\"restrict\">Mugatu</string>\n    <string name=\"unrestrict\">Muga kendu</string>\n    <string name=\"mute_stories\">Mututu istorioak</string>\n    <string name=\"mute_posts\">Mututu bidalketak</string>\n    <string name=\"unmute_stories\">Desmututu istorioak</string>\n    <string name=\"unmute_posts\">Desmututu bidalketak</string>\n    <string name=\"remove_follower\">Kendu jarraitzailea</string>\n    <string name=\"bio_copy\">Kopiatu biografia</string>\n    <string name=\"bio_translate\">Itzuli biografia</string>\n    <string name=\"status_mutual\">Alde bikoa</string>\n    <string name=\"status_following\">Jarraituak</string>\n    <string name=\"status_follower\">Jarraitzaile</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Kontuak</string>\n    <string name=\"dialog_export_settings\">Ezarpenak</string>\n    <string name=\"dialog_export_favorites\">Gogokoak</string>\n    <string name=\"dialog_import_success\">Ongi inportatu da!</string>\n    <string name=\"dialog_import_failed\">Inportatzeak huts egin du!</string>\n    <string name=\"dialog_export_success\">Ongi esportatu da!</string>\n    <string name=\"dialog_export_failed\">Esportatzeak huts egin du!</string>\n    <string name=\"refresh\">Freskatu</string>\n    <string name=\"get_cookies\">Eskuratu cookieak</string>\n    <string name=\"time_settings_title_custom\">Erabili pertsonalizatutako formatua</string>\n    <string name=\"time_settings_title_separator\">Bereizlea</string>\n    <string name=\"time_settings_title_time_format\">Ordu-formatua</string>\n    <string name=\"time_settings_title_date_format\">Data-formatua</string>\n    <string name=\"time_settings_title_preview\">Aurrebista</string>\n    <string name=\"time_settings_swap_time\">Aldatu ordua eta dataren posizioa</string>\n    <string name=\"quick_access_cannot_delete_curr\">Ezin da unean erabiltzen ari zaren kontua ezabatu</string>\n    <string name=\"quick_access_confirm_delete\">Ziur zaude \\\"%s\\\" ezabatu nahi duzula?</string>\n    <string name=\"open_profile\">Ireki profila</string>\n    <string name=\"view_story\">Ikusi istorioa</string>\n    <string name=\"view_pfp\">Ikusi profil-irudia</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Mezu mota ez da onartzen</string>\n    <string name=\"dms_inbox_unsend\">Ezabatu mezua</string>\n    <string name=\"dms_inbox_giphy\">Ikusi GIPHYn</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s(e)k irudia partekatu du</string>\n    <string name=\"dms_inbox_shared_video\">%s(e)k bideoa partekatu du</string>\n    <string name=\"dms_inbox_shared_message\">%s(e)k mezua bidali du</string>\n    <string name=\"dms_inbox_shared_gif\">%s(e)k gifa partekatu du</string>\n    <string name=\"dms_inbox_shared_sticker\">%s(e)k eranskina partekatu du</string>\n    <string name=\"dms_inbox_shared_profile\">%s(e)k profila partekatu du: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s(e)k kokalekua partekatu du: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s(e)k @%s(r)en istorioa partekatu du</string>\n    <string name=\"dms_inbox_shared_voice\">%s(e)k ahots-mezua bidali du</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s(e)k @%s(r)en IGTV bideoa partekatu du</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Bere istorioari erantzun diozu: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s(e)k zure istorioari erantzun dio: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Bere istorioari erreakzionatu diozu: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s(e)k zure istorioari erreakzionatu dio: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">\\@%s zure istorioan aipatu duzu</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s(e)k bere istorioan aipatu zaitu</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Multimedia mota ezezaguna</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Multimedia iraungi da!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Iritsi da</string>\n    <string name=\"dms_inbox_raven_media_sent\">Bidalita</string>\n    <string name=\"dms_inbox_raven_media_opened\">Irekita</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Berriro erreproduzituta</string>\n    <string name=\"dms_inbox_raven_media_sending\">Bidaltzen…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blokeatuta</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Iradokita</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Pantaila-argazkia aterata</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Ez da iritsi</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Sakatu eta mantendu audioa grabatzeko</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Utzi txata</string>\n    <string name=\"dms_action_leave_question\">Utzi txat hau?</string>\n    <string name=\"dms_action_kick\">Bota</string>\n    <string name=\"dms_left_users\">Joan diren erabiltzaileak</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Erabiltzaile baliogabea</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Fetching post(s)</string>\n    <string name=\"downloader_complete\">Deskarga burutu da</string>\n    <string name=\"downloader_preparing\">Deskargatzeko prestatzen…</string>\n    <string name=\"downloader_downloading_post\">Bidalketa deskargatzen…</string>\n    <string name=\"downloader_downloading_media\">Multimedia deskargatzen</string>\n    <string name=\"downloader_unknown_error\">Errore ezezaguna gertatu da</string>\n    <string name=\"downloader_error_creating_folder\">Errorea karpeta sortzean!</string>\n    <string name=\"downloader_error_download_file\">Errorea fitxategia deskargatzean</string>\n    <string name=\"comment_viewer_translate_comment\">Itzuli iruzkina</string>\n    <string name=\"comment_viewer_delete_comment\">Ezabatu iruzkina</string>\n    <string name=\"followers_type_followers\">Jarraitzaileak</string>\n    <string name=\"followers_type_following\">Jarraituak</string>\n    <string name=\"followers_compare\">Jarraitzaileak eta jarraituak konparatzen</string>\n    <string name=\"followers_both_following\">Elkarri jarraitzen diozue</string>\n    <string name=\"followers_not_following\">ez jarraitzen %s</string>\n    <string name=\"followers_not_follower\">%s(e)k ez zaitu jarraitzen</string>\n    <string name=\"login_error_loading_cookies\">Errorea cookieak kargatzean</string>\n    <string name=\"comment_hint\">Idatzi iruzkin berria…</string>\n    <string name=\"liked_notif\">Zure bidalketa atsegin du</string>\n    <string name=\"comment_notif\">Zure bidalketan iruzkindu du:</string>\n    <string name=\"follow_notif\">Zu jarraitzen hasi da</string>\n    <string name=\"tagged_notif\">Bidalketa batean etiketatu zaitu</string>\n    <string name=\"request_notif\">Zu jarraitzeko eskaera egin du</string>\n    <string name=\"request_approve\">Onartu eskaera</string>\n    <string name=\"request_reject\">Baztertu eskaera</string>\n    <string name=\"share_public_post\">Partekatu bidalketa publiko hau…</string>\n    <string name=\"share_private_post\">Bidalketa pribatua da. Partekatu ikus dezaketen horiekin.</string>\n    <string name=\"discover_empty\">Kategoria hau hutsik dago…</string>\n    <string name=\"update_available\">Eguneratze bat eskuragarri dago (%s)</string>\n    <string name=\"updated\">Eskerrik asko Barinsta eguneratzeagatik!</string>\n    <string name=\"crash_title\">Aplikazioa kraskatu da</string>\n    <string name=\"crash_descr\">Hara! Aplikazioa kraskatu da, baina ez kezkatu, errore-txostena garatzaileari bidali diezaiokezu arazoa konpondu dezan.</string>\n    <string name=\"action_notif\">Jarduera</string>\n    <string name=\"action_archive\">Istorio-artxiboa</string>\n    <string name=\"action_ayml\">Iradokitutako erabiltzaileak</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d jarraitzaile</string>\n    <string name=\"activity_count_comments\">%d iruzkin</string>\n    <string name=\"activity_count_commentlikes\">%d iruzkin-atsegite</string>\n    <string name=\"activity_count_usertags\">%d erabiltzaile-etiketak</string>\n    <string name=\"activity_count_likes\">%d atsegite</string>\n    <string name=\"activity_count_poy\">Ateratzen zaren %d argazki</string>\n    <string name=\"activity_count_requests\">%d jarraipen-eskari</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">Jarioa</string>\n    <string name=\"profile\">Profila</string>\n    <string name=\"more\">Gehiago</string>\n    <string name=\"title_dm\">MZ</string>\n    <string name=\"number_selected\">%d hautatua</string>\n    <string name=\"logout_success\">Saioa ongi itxi da!</string>\n    <string name=\"dm_thread_info\">Informazioa</string>\n    <string name=\"mark_as_seen\">Markatu ikusita gisa</string>\n    <string name=\"version\">Bertsioa</string>\n    <string name=\"pref_start_screen\">Hasierako pantaila</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Erakutsi teklatua bilaketan</string>\n    <string name=\"pref_category_general\">Orokorra</string>\n    <string name=\"pref_category_theme\">Gaia</string>\n    <string name=\"pref_category_downloads\">Deskargak</string>\n    <string name=\"pref_category_locale\">Lokala</string>\n    <string name=\"account\">Kontua</string>\n    <string name=\"account_hint\">Uneko saio-hasiera ez dabil? Gehitu kontua berriro.</string>\n    <string name=\"add_account\">Gehitu kontua</string>\n    <string name=\"about_category_license\">Lizentzia (Ingelesa soilik)</string>\n    <string name=\"about_documentation\">Ikusi gure webgunea</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">Ikusi iturburu kodea GitHuben</string>\n    <string name=\"about_repository_summary\">Kodea ikusi, parte hartu eta gozatu!</string>\n    <string name=\"about_feedback\">Bidali atzeraelikadura eposta bidez</string>\n    <string name=\"about_category_3pt\">Hirugarrenen atribuzioak</string>\n    <string name=\"reminder\">Oroigarria</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">Zuria</string>\n    <string name=\"dark_black_theme\">Beltza</string>\n    <string name=\"light_theme_settings\">Gai argia</string>\n    <string name=\"dark_theme_settings\">Gai iluna</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Akeita</string>\n    <string name=\"dark_material_dark_theme\">Beltz materiala</string>\n    <string name=\"added_to_favs\">Gogokoetara gehituta!</string>\n    <string name=\"add_to_favorites\">Gehitu gogokoetara</string>\n    <string name=\"accounts\">Kontuak</string>\n    <string name=\"hashtags\">Traolak</string>\n    <string name=\"locations\">Kokalekuak</string>\n    <string name=\"unknown\">Ezezaguna</string>\n    <string name=\"removed_from_favs\">Gogokoetatik kenduta!</string>\n    <string name=\"backup_and_restore\">Babeskopia eta leheneratzea</string>\n    <string name=\"auto_backup\">Babeskopia automatikoa</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Gaitu babeskopia automatikoa</string>\n    <string name=\"manual_backup\">Eskuzko babeskopia</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">Fitxategia:</string>\n    <string name=\"enter_password\">Sartu pasahitza</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">Ezarri</string>\n    <string name=\"save\">Gorde</string>\n    <string name=\"caption\">Argazki-oina</string>\n    <string name=\"edit_caption\">Editatu argazki-oina</string>\n    <string name=\"translate_caption\">Itzuli argazki-oina</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"liking\">Atsegiten…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Atsegitea kentzen…</string>\n    <string name=\"controls\">Kontrolak</string>\n    <string name=\"saving\">Gordetzen…</string>\n    <string name=\"removing\">Kentzen…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">Deskargatzen…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d of %2$d</string>\n    <string name=\"delete\">Ezabatu</string>\n    <string name=\"comment\">Iruzkina</string>\n    <string name=\"layout\">Antolamendua</string>\n    <string name=\"feed_stories\">Istorioen jarioa</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Partekatu</string>\n    <string name=\"layout_style\">Diseinua</string>\n    <string name=\"column_count\">Zutabe kopurua</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Erakutsi izenak</string>\n    <string name=\"show_avatars\">Erakutsi avatarrak</string>\n    <string name=\"avatar_size\">Avatarraren tamaina</string>\n    <string name=\"corners\">Ertzak</string>\n    <string name=\"show_grid_gap\">Erakutsi sareta-tartea</string>\n    <string name=\"post_not_found\">Bidalketa ez da aurkitu!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galeria</string>\n    <string name=\"camera\">Kamera</string>\n    <string name=\"all_photos\">Argazki guztiak</string>\n    <string name=\"all_media\">Multimedia guztia</string>\n    <string name=\"all_videos\">Bideo guztiak</string>\n    <string name=\"brightness\">Distira</string>\n    <string name=\"contrast\">Kontrastea</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturazioa</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Esposizioa</string>\n    <string name=\"center\">Erdiratu</string>\n    <string name=\"color\">Kolorea</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">End</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Berrezarri</string>\n    <string name=\"crop\">Ebaki</string>\n    <string name=\"normal\">Normala</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">Ikusaldi %d</item>\n        <item quantity=\"other\">%d ikusaldi</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">Istorio %s</item>\n        <item quantity=\"other\">%s istorio</item>\n    </plurals>\n    <string name=\"details\">Xehetasunak</string>\n    <string name=\"title\">Izenburua</string>\n    <string name=\"members\">Kideak</string>\n    <string name=\"admin\">Administratzailea</string>\n    <string name=\"inviter\">Gonbidatzailea</string>\n    <string name=\"mute_messages\">Mututu mezuak</string>\n    <string name=\"mute_mentions\">Mututu aipamenak</string>\n    <string name=\"add_members\">Gehitu kideak</string>\n    <string name=\"search\">Bilatu</string>\n    <string name=\"done\">Eginda</string>\n    <string name=\"dms_action_make_admin\">Ezarri Admin</string>\n    <string name=\"dms_action_remove_admin\">Kendu administratzailea</string>\n    <string name=\"edit_unsuccessful\">Edit was unsuccessful</string>\n    <string name=\"message\">Mezua</string>\n    <string name=\"tap_to_remove\">Ukitu kentzeko</string>\n    <string name=\"forward\">Birbidali</string>\n    <string name=\"forward_outgoing\">Mezu bat birbidali duzu</string>\n    <string name=\"forward_incoming\">Birbidalitako mezua</string>\n    <string name=\"add\">Gehitu</string>\n    <string name=\"send\">Bidali</string>\n    <string name=\"replying_to_yourself\">Zure buruari erantzuten</string>\n    <string name=\"replying_to_user\">%s(r)i erantzuten</string>\n    <string name=\"replied_to_yourself\">Zure buruari erantzun diozu</string>\n    <string name=\"replied_you\">Erantzun duzu</string>\n    <string name=\"replied_you_group\">%s(r)i erantzun diozu</string>\n    <string name=\"replied_group\">Replied to %s</string>\n    <string name=\"replied_to_you\">Erantzun dizu</string>\n    <string name=\"replied_to_themself\">Bere buruari erantzun dio</string>\n    <string name=\"reacted_story_outgoing\">Bere istorioari erreakzionatu diozu</string>\n    <string name=\"reacted_story_incoming\">Zure istorioari erreakzionatu dio</string>\n    <string name=\"mentioned_story_outgoing\">You mentioned them in your story</string>\n    <string name=\"mentioned_story_incoming\">Bere istorioan aipatu zaitu</string>\n    <string name=\"replied_story_outgoing\">Bere istorioari erantzun diozu</string>\n    <string name=\"replied_story_incoming\">Zure istorioari erantzun dio</string>\n    <string name=\"raven_image_expired\">Irudia iraungitu da</string>\n    <string name=\"raven_image_info\">Irudia ikusi bezain pronto iraungituko da</string>\n    <string name=\"raven_video_expired\">Bideoa iraungitu da</string>\n    <string name=\"raven_video_info\">Bideoa ikusi bezain pronto iraungituko da</string>\n    <string name=\"raven_msg_expired\">Mezua iraungitu da</string>\n    <string name=\"raven_msg_info\">Mezua ikusi bezain pronto iraungituko da</string>\n    <string name=\"story_share\">\\@%s(r)en istorioa</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Argazkia</string>\n    <string name=\"video\">Bideoa</string>\n    <string name=\"voice_message\">Ahots-mezua</string>\n    <string name=\"post\">Bidalketa</string>\n    <string name=\"approval_required_for_new_members\">Batzeko onarpena behar da</string>\n    <string name=\"requests\">Eskaerak</string>\n    <string name=\"admins_only\">Administratzaileak soilik</string>\n    <string name=\"added_by\">%s(e)k gehituta</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">Amaitu txata</string>\n    <string name=\"dms_action_end_question\">Amaitu txata?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Zain dauden eskariak</string>\n    <string name=\"accept_request_from_user\">Onartu %1s(e)n (%2s) eskaria?</string>\n    <string name=\"decline\">Baztertu</string>\n    <string name=\"accept\">Onartu</string>\n    <string name=\"you\">Zu</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Istorioak</string>\n    <string name=\"pref_category_dm\">MZ</string>\n    <string name=\"pref_category_notifications\">Jakinarazpenak</string>\n    <string name=\"pref_category_post\">Bidalketa</string>\n    <string name=\"enable_dm_notifications\">Gaitu MZen jakinarazpenak</string>\n    <string name=\"enable_dm_auto_refesh\">Freskatu jarioa automatikoki</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">segundo</string>\n    <string name=\"mins\">minutu</string>\n    <string name=\"search_giphy\">Bilatu GIPHYn</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Ikusita gisa markatuta</string>\n    <string name=\"delete_unsuccessful\">Ez da ongi deskargatu</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Errorea</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Saioa hastea beharrezkoa da!</string>\n    <string name=\"inactive_user\">Erabiltzailea ez dago aktibo!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Saltatu eguneratze hau</string>\n    <string name=\"on_latest_version\">Jadanik azken bertsioa duzu</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Kopiatu argazki-oina</string>\n    <string name=\"copy_reply\">Kopiatu erantzuna</string>\n    <string name=\"restore\">Leheneratu</string>\n    <string name=\"backup\">Babeskopia</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">Ez da karpetarik hautatu</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Arrakasta! Itxaron, aplikazioa hasten ari da…</string>\n    <string name=\"barinsta_folder\">Barinsta karpeta</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Azkenak</string>\n    <string name=\"clear\">Garbitu</string>\n    <string name=\"no_external_map_app\">Ez da mapa-aplikaziorik aurkitu!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">Irudirik ez da aurkitu!</string>\n    <string name=\"swipe_up_confirmation\">Ziur zaude esteka hau ireki nahi duzula?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Partekatu MZ bidez</string>\n    <string name=\"share_link\">Partekatu esteka…</string>\n    <string name=\"slide_to_cancel\">Irristatu uzteko</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>پیش فرض سیستم</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>خودکار / پیروی از سیستم</item>\n        <item>خودکار / پیرو باتری</item>\n        <item>تاریک</item>\n        <item>روشن</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>پیش‌فرض اینستاگرام (خوانده نشده سپس خوانده شده)</item>\n        <item>از جدیدترین به قدیمی ترین</item>\n        <item>از قدیمی ترین به جدید ترین</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>هیچکدام</item>\n        <item>\\@</item>\n        <item>در</item>\n        <item>در</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>ثانیه</item>\n        <item>دقیقه</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">درباره</string>\n    <string name=\"action_dms\">پیام‌های سرراست</string>\n    <string name=\"action_settings\">ساماندهی</string>\n    <string name=\"action_download\">بارگیری</string>\n    <string name=\"action_search\">جستجوی نام کاربری…</string>\n    <string name=\"action_compare\">مقایسه</string>\n    <string name=\"clipboard_error\">خطا هنگام کپی متن</string>\n    <string name=\"clipboard_copied\">در بریده‌دان رونوشت شد!</string>\n    <string name=\"report\">گزارش</string>\n    <string name=\"set_password\">محافظت از پوشه با گذرواژه</string>\n    <string name=\"password_no_max\">گذرواژه</string>\n    <string name=\"ok\">خوب</string>\n    <string name=\"yes\">بله</string>\n    <string name=\"cancel\">لغو</string>\n    <string name=\"no\">نه</string>\n    <string name=\"confirm\">پذیرش</string>\n    <string name=\"title_favorites\">علاقه مندی ها</string>\n    <string name=\"title_discover\">کاوش</string>\n    <string name=\"title_comments\">دیدگاه‌ها</string>\n    <string name=\"title_replies\">پاسخ ها</string>\n    <string name=\"title_notifications\">فعالیت</string>\n    <string name=\"update_check\">بررسی بروزرسانی هنگام آغاز برنامه</string>\n    <string name=\"flag_secure\">مسدود کردن تصویر از صفحه&amp; پیش‌نمایش برنامه</string>\n    <string name=\"download_user_folder\">بارگیری پست ها در پوشه های به نام کاربر</string>\n    <string name=\"download_prepend_username\">چسباندن نام کاربری به ابتدای نام پوشه</string>\n    <string name=\"mark_as_seen_setting\">نشان کرد استوری ها به عنوان دیده شده بعد از دیدن</string>\n    <string name=\"mark_as_seen_setting_summary\">نویسنده استوری می داند که شما آن را دیده اید</string>\n    <string name=\"hide_muted_reels_setting\">پنهان کردن داستان‌های بی‌صدا شده از خوراک‌خوان</string>\n    <string name=\"dm_mark_as_seen_setting\">نشان کردن پیام خصوصی بعنوان دیده شده بعد از دیدن</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">کاربران دیگر خواهند فهمید شما این را دیده اید</string>\n    <string name=\"autoplay_stories_setting\">پخش خودکار فیلم‌ها در داستان ها</string>\n    <string name=\"story_list_setting\">نمایش فهرست داستان به صورت پیش فرض</string>\n    <string name=\"story_list_setting_summary\">برای مشاهده داستان ها</string>\n    <string name=\"activity_setting\">روشن کردن آگهداد فعالیت ها</string>\n    <string name=\"story_sort_setting\">مرتب کردن خوراک‌خوان داستان‌ها</string>\n    <string name=\"error_loading_profile\">خطای بارگذاری نمایه! آیا نام کاربری درست است؟ اگر بله، ممکن است که محدود شده باشید.</string>\n    <string name=\"error_loading_hashtag\">خطای بارگذاری هشتگ! آیا نام معتبر است؟</string>\n    <string name=\"error_loading_location\">خطای بارگذاری مکان! URL معتبر است؟</string>\n    <string name=\"error_creating_folders\">خطا در ساخت پوشه(ها).</string>\n    <string name=\"select_folder\">انتخاب پوشه</string>\n    <string name=\"theme_settings\">پوسته</string>\n    <string name=\"select_language\">زبان</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\n پست</item>\n        <item quantity=\"other\">%s\\nپست‌ها</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s فرسته</item>\n        <item quantity=\"other\">%s فرسته</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nFollower</item>\n        <item quantity=\"other\">%s\\nدنبال کننده ها</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nدنبال کننده ها</string>\n    <string name=\"post_viewer_autoplay_video\">پخش خودکار فیلم ها</string>\n    <string name=\"post_viewer_background_play\">ادامه‌ی ویدئوها در پشت‌صفحه</string>\n    <string name=\"post_viewer_background_play_summary\">زمانی که برنامه خارج از تمرکز است مکث ویدئو را نزنید</string>\n    <string name=\"post_viewer_muted_autoplay\">همیشه فیلم هارو بی صدا کن</string>\n    <string name=\"post_viewer_show_captions\">همیشه متن پست ها را نمایش بده</string>\n    <string name=\"post_viewer_download_dialog_title\">انتخاب کن چی دانلود کنی</string>\n    <string name=\"post_viewer_download_current\">کنونی</string>\n    <string name=\"post_viewer_download_album\">آلبوم کامل</string>\n    <string name=\"show_stories\">نمایش استوری ها</string>\n    <string name=\"no_more_stories\">استوری بیشتری نیست!</string>\n    <string name=\"view_post\">دیدن فرسته</string>\n    <string name=\"story_poll\">نظرسنجی</string>\n    <string name=\"answered_story\">جواب درست است!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"other\">%d پاسخ میانگین %s</item>\n    </plurals>\n    <string name=\"slider_answer\">پاسخ شما: %s</string>\n    <string name=\"reply_story\">پاسخ به استوری</string>\n    <string name=\"reply_hint\">پاسخ…</string>\n    <string name=\"story_quiz\">نظرسنجی</string>\n    <string name=\"story_slider\">لغزنده</string>\n    <string name=\"story_quizzed\">در حال حاظر شما رای داده اید!</string>\n    <string name=\"story_mentions\">یادآوری ها</string>\n    <string name=\"story_question\">پرسش</string>\n    <string name=\"priv_acc\">این اکانت شخصی است</string>\n    <string name=\"priv_acc_confirm\">شما امکان دسترسی به پست ها را بعد از آنفالو کردن ندارید!\nمطمن هستید؟</string>\n    <string name=\"are_you_sure\">آیا مطمئن هستید؟</string>\n    <string name=\"no_acc\">می توانید توسط (بیشتر) وارد شوید -&gt; حساب در گوشه پایین سمت راست یا می توانید حساب های عمومی را بدون ورود به سیستم ببینید!</string>\n    <string name=\"empty_acc\">این اکانت پستی ندارد</string>\n    <string name=\"empty_list\">چنین پست هایی نیست!</string>\n    <string name=\"login\">ورود</string>\n    <string name=\"logout\">خروج</string>\n    <string name=\"logout_summary\">گشت و گذار ناشناس در اینستاگرام</string>\n    <string name=\"remove_all_acc\">حذف همه حساب ها</string>\n    <string name=\"remove_all_acc_warning\">این تمام حساب های اضافه شده به برنامه را حذف خواهد کرد!\\n برای حذف کردن فقط یک حساب، مدت طولانی روی حساب ضربه بزنید از دیالوگ تعویض حساب.\\n میخواهید ادامه دهید؟</string>\n    <string name=\"time_settings\">فرمت تاریخ</string>\n    <string name=\"saved_create_collection\">ایجاد مجموعه جدید</string>\n    <string name=\"edit_collection\">ویرایش نام مجموعه</string>\n    <string name=\"delete_collection\">حذف مجموعه</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">افزودن به مجموعه…</string>\n    <string name=\"remove_from_collection\">برداشتن از مجموعه</string>\n    <string name=\"liked\">پسندیده شد</string>\n    <string name=\"saved\">ذخیره شد</string>\n    <string name=\"tagged\">تگ شده</string>\n    <string name=\"dm_person\">پیام</string>\n    <string name=\"follow\">پی‌گرفتن</string>\n    <string name=\"unfollow\">پایان پی‌گرفتن</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">برگزیده‌</string>\n    <string name=\"block\">مسدود</string>\n    <string name=\"unblock\">رفع مسدودیت</string>\n    <string name=\"restrict\">محدود کردن</string>\n    <string name=\"unrestrict\">حذف محدودیت</string>\n    <string name=\"mute_stories\">بی‌صدا کردن داستان‌ها</string>\n    <string name=\"mute_posts\">بی‌صدا کردن پست ها</string>\n    <string name=\"unmute_stories\">صدادار کردن داستان ها</string>\n    <string name=\"unmute_posts\">صدادار کردن پست ها</string>\n    <string name=\"remove_follower\">پاک کردن دنبال کننده</string>\n    <string name=\"bio_copy\">رونوشت شرح حال</string>\n    <string name=\"bio_translate\">ترجمه شرح حال</string>\n    <string name=\"status_mutual\">متقابل</string>\n    <string name=\"status_following\">پیگیری</string>\n    <string name=\"status_follower\">پیگیر</string>\n    <string name=\"map\">نقشه</string>\n    <string name=\"dialog_export_accounts\">هساب‌ها</string>\n    <string name=\"dialog_export_settings\">ساماندهی</string>\n    <string name=\"dialog_export_favorites\">علاقه مندی ها</string>\n    <string name=\"dialog_import_success\">با موفقیت وارد شد!</string>\n    <string name=\"dialog_import_failed\">وارد کردن موفقیت آمیز نبود!</string>\n    <string name=\"dialog_export_success\">با موفقیت پشتیبان گیری شد!</string>\n    <string name=\"dialog_export_failed\">پشتبان گیری با موفقیت نبود!</string>\n    <string name=\"refresh\">تازه سازی</string>\n    <string name=\"get_cookies\">گرفتن کوکی ها</string>\n    <string name=\"time_settings_title_custom\">استفاده از فرمت سفارشی</string>\n    <string name=\"time_settings_title_separator\">تفکیک کننده</string>\n    <string name=\"time_settings_title_time_format\">فرمت زمان</string>\n    <string name=\"time_settings_title_date_format\">فرمت تاریخ</string>\n    <string name=\"time_settings_title_preview\">پیش نمایش</string>\n    <string name=\"time_settings_swap_time\">موقعیت زمان و تاریخ را عوض کنید</string>\n    <string name=\"quick_access_cannot_delete_curr\">نمیتواند حذف شود این اکانت در حال استفاده است</string>\n    <string name=\"quick_access_confirm_delete\">آیا مطمن هستید میخواهید حذف کنید \\'%s\\'؟</string>\n    <string name=\"open_profile\">باز کردن پروفایل</string>\n    <string name=\"view_story\">مشاهده داستان</string>\n    <string name=\"view_pfp\">دیدن عکس پروفایل</string>\n    <string name=\"dms_inbox_raven_message_unknown\">نوع پیام پشتیبانی نشده</string>\n    <string name=\"dms_inbox_unsend\">بازگرداندن پیام</string>\n    <string name=\"dms_inbox_giphy\">مشاهده در GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s پست را با @%s به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_image\">%s تصویری به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_video\">%s فیلمی به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_message\">%s پیامی فرستاد</string>\n    <string name=\"dms_inbox_shared_gif\">%s گیفی به اشتراک گذشت</string>\n    <string name=\"dms_inbox_shared_sticker\">%s برچسبی به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_profile\">%s نمایه ای را به اشتراک گذاشت: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s مکانی را به اشتراک گذاشت: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s داستان هایلایتِ @%s را به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_story\">%s داستانی از @%s را به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_voice\">%s پیامی صوتی فرستاد</string>\n    <string name=\"dms_inbox_shared_clip\">%s کلیپی از @%s را به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_shared_igtv\">%s فیلم IGTV از @%s را به اشتراک گذاشت</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">شما به داستانشان پاسخ دادید: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s به داستانتان پاسخ داد: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">شما به داستانشان واکنش نشان دادید: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s به داستانتان واکنش نشان داد: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">شما در داستانتان از @%s نام بردید</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s از شما در داستانشان نام برده است</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>نوع رسانه ناشناخته است</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">رسانه منقضی شده!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">ارسال شد</string>\n    <string name=\"dms_inbox_raven_media_sent\">ارسال شد</string>\n    <string name=\"dms_inbox_raven_media_opened\">باز شده</string>\n    <string name=\"dms_inbox_raven_media_replayed\">پاسخ داده شده</string>\n    <string name=\"dms_inbox_raven_media_sending\">در حال ارسال…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">بلاک شده</string>\n    <string name=\"dms_inbox_raven_media_suggested\">پیشنهاد شده</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">اسکرین شات گرفته شده</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">نمیتواند ارسال شود</string>\n    <string name=\"dms_thread_message_hint\">پیام…</string>\n    <string name=\"dms_thread_audio_hint\">برای ضبط صدا، بفشارید و نگه دارید</string>\n    <string name=\"dms_thread_updating\">در حال بروزرسانی…</string>\n    <string name=\"dms_action_leave\">خروج از گفتگو</string>\n    <string name=\"dms_action_leave_question\">این گفتگو را ترک میکنید؟</string>\n    <string name=\"dms_action_kick\">اخراج</string>\n    <string name=\"dms_left_users\">کاربران ترک کرده</string>\n    <string name=\"dms_ERROR_INVALID_USER\">کاربر نامعتبر</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">اینستاگرام اجاری بارگذاری فیلم‌های طولانی تر از ۶۰ ثانیه را در خصوصی نمی‌دهد.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">اینستاگرام اجازه بارگذاری صداهای طولانی تر از ۶۰ ثانیه را نمی دهد.</string>\n    <string name=\"direct_download_loading\">آوردن پست (ها)</string>\n    <string name=\"downloader_complete\">دانلود کامل شده</string>\n    <string name=\"downloader_preparing\">آماده سازی برای بارگیری…</string>\n    <string name=\"downloader_downloading_post\">دریافت پیک…</string>\n    <string name=\"downloader_downloading_media\">دانلود رسانه ها</string>\n    <string name=\"downloader_unknown_error\">خطای ناشناخته رخ داده است!!!</string>\n    <string name=\"downloader_error_creating_folder\">خطا در ایجاد پوشه!</string>\n    <string name=\"downloader_error_download_file\">خطا در دانلود فایل</string>\n    <string name=\"comment_viewer_translate_comment\">گرداندن دیدگاه</string>\n    <string name=\"comment_viewer_delete_comment\">حذف دیدگاه</string>\n    <string name=\"followers_type_followers\">پیگیران</string>\n    <string name=\"followers_type_following\">پیگیری</string>\n    <string name=\"followers_compare\">مقایسه دنبال کنندگان &amp; زیر</string>\n    <string name=\"followers_both_following\">هردو یکدیگر را دنبال میکنند</string>\n    <string name=\"followers_not_following\">دنبال نمیکند %s</string>\n    <string name=\"followers_not_follower\">%s دنبال نمی کنند</string>\n    <string name=\"login_error_loading_cookies\">خطای بالا آوردن کوکی ها</string>\n    <string name=\"comment_hint\">نوشتن یک دیدگاه نو…</string>\n    <string name=\"liked_notif\">پیک شما را پسندید</string>\n    <string name=\"comment_notif\">کامنت رو پست شما گذاشته:</string>\n    <string name=\"follow_notif\">شروع به دنبال کردن شما کرده</string>\n    <string name=\"tagged_notif\">شما را در یک پست، تگ کرده است</string>\n    <string name=\"request_notif\">درخواست دنبال کردن شما را کرده</string>\n    <string name=\"request_approve\">قبول درخواست</string>\n    <string name=\"request_reject\">رد درخواست</string>\n    <string name=\"share_public_post\">همرسانی این پیک همگانی به…</string>\n    <string name=\"share_private_post\">این یک پست شخصی است! با کسانی که می توانند ببینند به اشتراک بگذارید.</string>\n    <string name=\"discover_empty\">این دسته به نوعی خالی است…</string>\n    <string name=\"update_available\">یک بروزرسانی آمده است! (%s)</string>\n    <string name=\"updated\">از شما بابت بروز رسانی Barinsta سپاسگزاریم!</string>\n    <string name=\"crash_title\">برنامه خراب شد</string>\n    <string name=\"crash_descr\">اوپسس.. برنامه خراب است، اما نگران نباشید شما میتوانید گزارش خطا را برای توسعه دهنده بفرستید برای کمک به او در درست کردن مشکل. (:</string>\n    <string name=\"action_notif\">فعالیت</string>\n    <string name=\"action_archive\">سابقه داستان‌ها</string>\n    <string name=\"action_ayml\">کاربران پیشنهادی</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">شما %d اعلان دارید</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d دنبال کننده‌</string>\n    <string name=\"activity_count_comments\">%d دیدگاه</string>\n    <string name=\"activity_count_commentlikes\">%d پسند دیدگاه</string>\n    <string name=\"activity_count_usertags\">%d تگ های کاربر</string>\n    <string name=\"activity_count_likes\">%d پسندها</string>\n    <string name=\"activity_count_poy\">%d تصاویر شما</string>\n    <string name=\"activity_count_requests\">%d درخواست دنبال کردن</string>\n    <string name=\"activity_notloggedin\">شما خارج شدید قبل از اینکه بروی این اعلان کلیک کنید؟</string>\n    <string name=\"feed\">پست</string>\n    <string name=\"profile\">نمایه</string>\n    <string name=\"more\">دیگر</string>\n    <string name=\"title_dm\">پیام خصوصی</string>\n    <string name=\"number_selected\">%d انتخاب شده</string>\n    <string name=\"logout_success\">خروج با موفقیت!</string>\n    <string name=\"dm_thread_info\">ازدایش</string>\n    <string name=\"mark_as_seen\">نشان کردن به عنوان دیده شده</string>\n    <string name=\"version\">نگارش</string>\n    <string name=\"pref_start_screen\">صفحه آغازین</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">نمایش صفحه کلید در جستجو</string>\n    <string name=\"pref_category_general\">فراگیر</string>\n    <string name=\"pref_category_theme\">پوسته</string>\n    <string name=\"pref_category_downloads\">دریافت‌ها</string>\n    <string name=\"pref_category_locale\">محل</string>\n    <string name=\"account\">هساب</string>\n    <string name=\"account_hint\">حساب کنونی کار نمی کند ؟ به سادگی دوباره حساب را اضافه کنید.</string>\n    <string name=\"add_account\">افزودن حساب</string>\n    <string name=\"about_category_license\">مجوز (فقط انگلیسی)</string>\n    <string name=\"about_documentation\">از وبسایت ما بازدید کنید</string>\n    <string name=\"about_documentation_summary\">پشتیبانی دریافت کنید ، بحث کنید ، با دیگران ملاقات کنید و از آن لذت ببرید!</string>\n    <string name=\"about_repository\">کد منبع ما را در GitHub ببینید</string>\n    <string name=\"about_repository_summary\">حسابرسی ، ستاره گذاری ، گزارش اشکالات ، مشارکت و سرگرمی (دوباره)!</string>\n    <string name=\"about_feedback\">ارسال بازخورد با ایمیل</string>\n    <string name=\"about_category_3pt\">اسناد شخص ثالث</string>\n    <string name=\"reminder\">یادآور</string>\n    <string name=\"reminder_summary\">لطفا با مسئولیت پذیری از این برنامه استفاده کنید. از تصاویر بارگیری شده فقط باید برای مقاصدی استفاده شود که طبق قوانین قابل اجرا است.</string>\n    <string name=\"light_white_theme\">سفید</string>\n    <string name=\"dark_black_theme\">سیاه</string>\n    <string name=\"light_theme_settings\">پوسته روشن</string>\n    <string name=\"dark_theme_settings\">پوسته تیره</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">باریستا</string>\n    <string name=\"dark_material_dark_theme\">متریال تیره</string>\n    <string name=\"added_to_favs\">به علاقه مندی ها افزوده شد!</string>\n    <string name=\"add_to_favorites\">افزودن به علاقمندی‌ها</string>\n    <string name=\"accounts\">هساب‌ها</string>\n    <string name=\"hashtags\">هشتگ ها</string>\n    <string name=\"locations\">جاها</string>\n    <string name=\"unknown\">ناشناخته</string>\n    <string name=\"removed_from_favs\">حذف از علاقمندی ها!</string>\n    <string name=\"backup_and_restore\">پشتیبان گیری &amp; بازیابی</string>\n    <string name=\"auto_backup\">پشتیبان‌گیری خودکار</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">فعال سازی پشتیبان گیری خودکار</string>\n    <string name=\"manual_backup\">پشتیبان گیری دستی</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">ایجاد فایل پشتیبان جدید</string>\n    <string name=\"restore_backup\">بازگردانی از فایل پشتیبان موجود</string>\n    <string name=\"file_chosen_label\">پرونده:</string>\n    <string name=\"enter_password\">رمز عبور را وارد کنید</string>\n    <string name=\"select_backup_file\">یک فایل پشتیبان گیری انتخاب کنید (.zaai/.backup)</string>\n    <string name=\"apply\">گماشتن</string>\n    <string name=\"save\">اندوختن</string>\n    <string name=\"caption\">Caption</string>\n    <string name=\"edit_caption\">Edit caption</string>\n    <string name=\"translate_caption\">Translate caption</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"liking\">پسندیدن…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">پایش</string>\n    <string name=\"saving\">درحال ذخیره کردن…</string>\n    <string name=\"removing\">در هذف…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">در دریافت…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d of %2$d</string>\n    <string name=\"delete\">هذف</string>\n    <string name=\"comment\">دیدگاه</string>\n    <string name=\"layout\">چیدمان</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">همرسانی</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">نمایش نام‌ها</string>\n    <string name=\"show_avatars\">Show avatars</string>\n    <string name=\"avatar_size\">اندازه آواتار</string>\n    <string name=\"corners\">کناره ها</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">پست پیدا نشد!</string>\n    <string name=\"no_external_app_url\">هیچ برنامه ای برای باز کردن ادرسها پیدا نشد</string>\n    <string name=\"gallery\">گالری</string>\n    <string name=\"camera\">فرتورانداز</string>\n    <string name=\"all_photos\">همۀ فرتورها</string>\n    <string name=\"all_media\">همۀ رسانه‌ها</string>\n    <string name=\"all_videos\">همه ویدئوها</string>\n    <string name=\"brightness\">روشنایی</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exposure</string>\n    <string name=\"center\">مرکز</string>\n    <string name=\"color\">رنگ</string>\n    <string name=\"start\">آغاز</string>\n    <string name=\"end\">پایان</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">۱۹۹۷</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">بازنشانی</string>\n    <string name=\"crop\">برش</string>\n    <string name=\"normal\">معمولی</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d view</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">جزئيات</string>\n    <string name=\"title\">عنوان</string>\n    <string name=\"members\">Members</string>\n    <string name=\"admin\">تارگردان</string>\n    <string name=\"inviter\">Inviter</string>\n    <string name=\"mute_messages\">Mute messages</string>\n    <string name=\"mute_mentions\">Mute mentions</string>\n    <string name=\"add_members\">Add members</string>\n    <string name=\"search\">جستـجو</string>\n    <string name=\"done\">انجام شد</string>\n    <string name=\"dms_action_make_admin\">Make Admin</string>\n    <string name=\"dms_action_remove_admin\">Remove as Admin</string>\n    <string name=\"edit_unsuccessful\">Edit was unsuccessful</string>\n    <string name=\"message\">پیام</string>\n    <string name=\"tap_to_remove\">Tap to remove</string>\n    <string name=\"forward\">Forward</string>\n    <string name=\"forward_outgoing\">You forwarded a message</string>\n    <string name=\"forward_incoming\">Forwarded a message</string>\n    <string name=\"add\">اضافه کردن</string>\n    <string name=\"send\">ارسال کردن</string>\n    <string name=\"replying_to_yourself\">Replying to yourself</string>\n    <string name=\"replying_to_user\">Replying to %s</string>\n    <string name=\"replied_to_yourself\">You replied to yourself</string>\n    <string name=\"replied_you\">You replied</string>\n    <string name=\"replied_you_group\">You replied to %s</string>\n    <string name=\"replied_group\">Replied to %s</string>\n    <string name=\"replied_to_you\">Replied to you</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">You reacted to their story</string>\n    <string name=\"reacted_story_incoming\">Reacted to your story</string>\n    <string name=\"mentioned_story_outgoing\">You mentioned them in your story</string>\n    <string name=\"mentioned_story_incoming\">Mentioned you in their story</string>\n    <string name=\"replied_story_outgoing\">You replied to their story</string>\n    <string name=\"replied_story_incoming\">Replied to your story</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">ویدئو منقضی شده است</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">پیام منقضی شده است</string>\n    <string name=\"raven_msg_info\">پیام وقتی دیده شود منقضی خواهد شد</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">فرتور</string>\n    <string name=\"video\">ویدئو</string>\n    <string name=\"voice_message\">پیام صوتی</string>\n    <string name=\"post\">فرسته</string>\n    <string name=\"approval_required_for_new_members\">Approval required to join</string>\n    <string name=\"requests\">درخواست ها</string>\n    <string name=\"admins_only\">فقط مدیران</string>\n    <string name=\"added_by\">اضافه شده توسط %s</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Decline</string>\n    <string name=\"accept\">Accept</string>\n    <string name=\"you\">شما</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">پیام سرراست</string>\n    <string name=\"pref_category_notifications\">آگهداد</string>\n    <string name=\"pref_category_post\">فرسته</string>\n    <string name=\"enable_dm_notifications\">Enable DM notifications</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">secs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Search GIPHY</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Par défaut (Système)</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automatique (Système)</item>\n        <item>Automatique (Batterie)</item>\n        <item>Sombre</item>\n        <item>Clair</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram par défaut (Non lu puis lu)</item>\n        <item>Du plus récent au plus ancien</item>\n        <item>Du plus ancien au plus récent</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Aucun</item>\n        <item>\\@</item>\n        <item>à</item>\n        <item>le</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>secondes</item>\n        <item>minutes</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">À propos</string>\n    <string name=\"action_dms\">Messages directs</string>\n    <string name=\"action_settings\">Paramètres</string>\n    <string name=\"action_download\">Téléchargement</string>\n    <string name=\"action_search\">Rechercher par nom d\\'utilisateur…</string>\n    <string name=\"action_compare\">Comparer</string>\n    <string name=\"clipboard_error\">Erreur lors de la copie des éléments</string>\n    <string name=\"clipboard_copied\">Copié dans le presse-papier !</string>\n    <string name=\"report\">Reporter</string>\n    <string name=\"set_password\">Protéger le fichier avec un mot de passe</string>\n    <string name=\"password_no_max\">Mot de passe</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Oui</string>\n    <string name=\"cancel\">Annuler</string>\n    <string name=\"no\">Non</string>\n    <string name=\"confirm\">Confirmer</string>\n    <string name=\"title_favorites\">Favoris</string>\n    <string name=\"title_discover\">Découvrir</string>\n    <string name=\"title_comments\">Commentaires</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Activité</string>\n    <string name=\"update_check\">Rechercher les mises à jours au démarrage</string>\n    <string name=\"flag_secure\">Bloquer les captures d\\'écran &amp; l\\'aperçu de l\\'application</string>\n    <string name=\"download_user_folder\">Télécharger les messages dans les dossiers des noms d\\'utilisateurs</string>\n    <string name=\"download_prepend_username\">Préfixer le nom d\\'utilisateur au nom de fichier</string>\n    <string name=\"mark_as_seen_setting\">Marquer les stories comme vues après consultation</string>\n    <string name=\"mark_as_seen_setting_summary\">L\\'auteur de la story saura que vous l\\'avez vue</string>\n    <string name=\"hide_muted_reels_setting\">Masquer les stories en sourdine du flux</string>\n    <string name=\"dm_mark_as_seen_setting\">Marquer les messages privés comme vus après consultation</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Les autres utilisateurs saurons que vous l\\'avez vu</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Activer les notifications d\\'activités</string>\n    <string name=\"story_sort_setting\">Tri du flux de stories</string>\n    <string name=\"error_loading_profile\">Erreur lors du chargement du profil! Le nom d\\'utilisateur est-il valide? Si c\\'est le cas, vous êtes peut-être limité à un débit.</string>\n    <string name=\"error_loading_hashtag\">Erreur lors du chargement du hashtag ! Le nom est-il valide ?</string>\n    <string name=\"error_loading_location\">Erreur lors du chargement de l\\'emplacement ! L\\'URL est-elle valide ?</string>\n    <string name=\"error_creating_folders\">Erreur de création de dossier(s) de téléchargement.</string>\n    <string name=\"select_folder\">Sélectionnez un dossier</string>\n    <string name=\"theme_settings\">Thème</string>\n    <string name=\"select_language\">Langues</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPublication</item>\n        <item quantity=\"other\">%s\\nPublications</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Publication</item>\n        <item quantity=\"other\">%s Publications</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nAbonné</item>\n        <item quantity=\"other\">%s\\nAbonnés</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nAbonnements</string>\n    <string name=\"post_viewer_autoplay_video\">Lecture automatique des vidéos</string>\n    <string name=\"post_viewer_background_play\">Continuer les vidéos en arrière-plan</string>\n    <string name=\"post_viewer_background_play_summary\">Ne pas mettre les vidéos en pause lorsque l\\'application est inactive</string>\n    <string name=\"post_viewer_muted_autoplay\">Toujours couper le son des vidéos</string>\n    <string name=\"post_viewer_show_captions\">Toujours afficher les sous-titres de publication</string>\n    <string name=\"post_viewer_download_dialog_title\">Sélectionnez ce que vous souhaitez télécharger</string>\n    <string name=\"post_viewer_download_current\">Actuel</string>\n    <string name=\"post_viewer_download_album\">Album complet</string>\n    <string name=\"show_stories\">Afficher les stories</string>\n    <string name=\"no_more_stories\">Plus de stories!</string>\n    <string name=\"view_post\">Aperçu</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Réponse envoyée !</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d réponse en moyenne %s</item>\n        <item quantity=\"other\">%d réponses en moyenne %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Votre réponse : %s</string>\n    <string name=\"reply_story\">Répondre à la story</string>\n    <string name=\"reply_hint\">Réponse…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Curseur</string>\n    <string name=\"story_quizzed\">Vous avez déjà répondu !</string>\n    <string name=\"story_mentions\">Mentions</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Compte privé</string>\n    <string name=\"priv_acc_confirm\">Vous ne pourrez plus accéder aux messages après avoir été désabonné! Êtes-vous sûr(e) ?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Vous pouvez vous connecter via Plus -&gt; de Compte en bas à droite ou vous pouvez consulter les comptes publics sans vous connecter !</string>\n    <string name=\"empty_acc\">Ce compte ne contient pas de posts</string>\n    <string name=\"empty_list\">Aucun post de ce genre !</string>\n    <string name=\"login\">Se connecter</string>\n    <string name=\"logout\">Se déconnecter</string>\n    <string name=\"logout_summary\">Parcourir Instagram de façon anonyme</string>\n    <string name=\"remove_all_acc\">Supprimer tous les comptes</string>\n    <string name=\"remove_all_acc_warning\">Ceci supprimera tous les comptes ajoutés sur l\\'application!\\nPour supprimer un seul compte, appuyez longuement sur le compte depuis la boîte de dialogue de changement de compte.\\nVoulez-vous continuer?</string>\n    <string name=\"time_settings\">Format de la date</string>\n    <string name=\"saved_create_collection\">Créer une nouvelle collection</string>\n    <string name=\"edit_collection\">Modifier le nom de la collection</string>\n    <string name=\"delete_collection\">Supprimer la collection</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Retirer de la collection</string>\n    <string name=\"liked\">Aimé</string>\n    <string name=\"saved\">Sauvegardé</string>\n    <string name=\"tagged\">Taggé</string>\n    <string name=\"dm_person\">Message</string>\n    <string name=\"follow\">Suivre</string>\n    <string name=\"unfollow\">Ne plus suivre</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Mettre en favoris</string>\n    <string name=\"block\">Bloquer</string>\n    <string name=\"unblock\">Débloquer</string>\n    <string name=\"restrict\">Restreindre</string>\n    <string name=\"unrestrict\">Retirer des restrictions</string>\n    <string name=\"mute_stories\">Mute les stories</string>\n    <string name=\"mute_posts\">Mute les publications</string>\n    <string name=\"unmute_stories\">Démute les stories</string>\n    <string name=\"unmute_posts\">Démutez les publications</string>\n    <string name=\"remove_follower\">Retirer l\\'abonné</string>\n    <string name=\"bio_copy\">Copier la bio</string>\n    <string name=\"bio_translate\">Traduire la bio</string>\n    <string name=\"status_mutual\">Mutuel</string>\n    <string name=\"status_following\">Abonnements</string>\n    <string name=\"status_follower\">Abonné</string>\n    <string name=\"map\">Carte</string>\n    <string name=\"dialog_export_accounts\">Comptes</string>\n    <string name=\"dialog_export_settings\">Paramètres</string>\n    <string name=\"dialog_export_favorites\">Favoris</string>\n    <string name=\"dialog_import_success\">Importé avec succès!</string>\n    <string name=\"dialog_import_failed\">Erreur d\\'importation!</string>\n    <string name=\"dialog_export_success\">Exporté avec succès!</string>\n    <string name=\"dialog_export_failed\">Erreur d\\'exportation!</string>\n    <string name=\"refresh\">Actualiser</string>\n    <string name=\"get_cookies\">Récupérer les cookies</string>\n    <string name=\"time_settings_title_custom\">Utiliser un format personnalisé</string>\n    <string name=\"time_settings_title_separator\">Séparateur</string>\n    <string name=\"time_settings_title_time_format\">Format de l\\'heure</string>\n    <string name=\"time_settings_title_date_format\">Format des dates</string>\n    <string name=\"time_settings_title_preview\">Prévisualiser</string>\n    <string name=\"time_settings_swap_time\">Permuter la date et l\\'heure</string>\n    <string name=\"quick_access_cannot_delete_curr\">Impossible de supprimer le compte actuellement utilisé</string>\n    <string name=\"quick_access_confirm_delete\">Êtes-vous sûr de vouloir supprimer \\'%s\\' ?</string>\n    <string name=\"open_profile\">Ouvrir le profil</string>\n    <string name=\"view_story\">Voir la story</string>\n    <string name=\"view_pfp\">Voir la photo de profil</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Type de message non pris en charge</string>\n    <string name=\"dms_inbox_unsend\">Annuler l\\'envoi du message</string>\n    <string name=\"dms_inbox_giphy\">Voir sur GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s a partagé un post de @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s a partagé une image</string>\n    <string name=\"dms_inbox_shared_video\">%s a partagé une vidéo</string>\n    <string name=\"dms_inbox_shared_message\">%s a envoyé un message</string>\n    <string name=\"dms_inbox_shared_gif\">%s a partagé un gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s a partagé un autocollant</string>\n    <string name=\"dms_inbox_shared_profile\">%s a partagé un profil : @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s a partagé une position : %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s a partagé une story mise en avant de @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s a partagé une story de @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s vous a envoyé un message vocal</string>\n    <string name=\"dms_inbox_shared_clip\">%s a partagé un clip de @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s a partagé une vidéo IGTV de @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Vous avez répondu à sa story : %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s A répondu à votre story : %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Vous avez répondu à sa story : %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s A réagi à votre story : %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Vous avez mentionné @%s dans votre histoire</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s vous a mentionné dans son histoire</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Type de média inconnu</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Média expiré!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Distribué</string>\n    <string name=\"dms_inbox_raven_media_sent\">Envoyé</string>\n    <string name=\"dms_inbox_raven_media_opened\">Ouvert</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Joué à nouveau</string>\n    <string name=\"dms_inbox_raven_media_sending\">Envoi en cours…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Bloqué</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggéré</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Capture d\\'écran effectuée</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Impossible de livrer</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Appuyer de manière prolongée pour enregistrer un audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Quitter la conversation</string>\n    <string name=\"dms_action_leave_question\">Quitter cette conversation ?</string>\n    <string name=\"dms_action_kick\">Éjecter</string>\n    <string name=\"dms_left_users\">Utilisateurs restants</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Utilisateur non valide</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram n\\'autorise pas le téléchargement de vidéos de plus de 60 secondes pour les DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram ne permet pas de télécharger des fichiers audio de plus de 60 secondes.</string>\n    <string name=\"direct_download_loading\">Téléchargement de(s) post(s)</string>\n    <string name=\"downloader_complete\">Téléchargement terminé</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Entrain de télécharger la publications…</string>\n    <string name=\"downloader_downloading_media\">Téléchargement du média en cours</string>\n    <string name=\"downloader_unknown_error\">Une erreur inconnue s\\'est produite!!!</string>\n    <string name=\"downloader_error_creating_folder\">Erreur de création de dossier !</string>\n    <string name=\"downloader_error_download_file\">Erreur de téléchargement du fichier</string>\n    <string name=\"comment_viewer_translate_comment\">Traduire le commentaire</string>\n    <string name=\"comment_viewer_delete_comment\">Supprimer le commentaire</string>\n    <string name=\"followers_type_followers\">Abonnés</string>\n    <string name=\"followers_type_following\">Abonnements</string>\n    <string name=\"followers_compare\">Comparer les abonnés et abonnements</string>\n    <string name=\"followers_both_following\">Abonnements en commun</string>\n    <string name=\"followers_not_following\">Comptes qui ne suivent pas %s</string>\n    <string name=\"followers_not_follower\">Comptes non suivis par %s</string>\n    <string name=\"login_error_loading_cookies\">Erreur de chargement des cookies</string>\n    <string name=\"comment_hint\">Écrire un nouveau commentaire…</string>\n    <string name=\"liked_notif\">A aimé votre publication</string>\n    <string name=\"comment_notif\">A commenté sur ta publication :</string>\n    <string name=\"follow_notif\">A commencé à vous suivre</string>\n    <string name=\"tagged_notif\">T\\'a cité dans une publication</string>\n    <string name=\"request_notif\">A demandé à vous suivre</string>\n    <string name=\"request_approve\">Approuver la demande</string>\n    <string name=\"request_reject\">Refuser la demande</string>\n    <string name=\"share_public_post\">Partage cette publication publique…</string>\n    <string name=\"share_private_post\">C\\'est post privé ! Partagez avec ceux qui peuvent le voir.</string>\n    <string name=\"discover_empty\">Cette catégorie est en quelque sorte vide…</string>\n    <string name=\"update_available\">Une mise-à-jour est disponible! (%s)</string>\n    <string name=\"updated\">Merci d\\'avoir mis à jour Barinsta !</string>\n    <string name=\"crash_title\">L\\'application a planté</string>\n    <string name=\"crash_descr\">Oups.. l\\'application a planté, mais ne vous inquiétez pas, vous pouvez envoyer un rapport d\\'erreur au développeur pour l\\'aider à résoudre le problème :)</string>\n    <string name=\"action_notif\">Activité</string>\n    <string name=\"action_archive\">Archive des story</string>\n    <string name=\"action_ayml\">Utilisateurs suggérés</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d abonné(e)s</string>\n    <string name=\"activity_count_comments\">%d commentaires</string>\n    <string name=\"activity_count_commentlikes\">%d j\\'aime(s) sur le commentaire</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d j\\'aime(s)</string>\n    <string name=\"activity_count_poy\">%d photos de vous</string>\n    <string name=\"activity_count_requests\">%d demandes d\\'abonnement</string>\n    <string name=\"activity_notloggedin\">Vous vous êtes déconnecté avant de cliquer sur cette notification?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Plus</string>\n    <string name=\"title_dm\">MP</string>\n    <string name=\"number_selected\">%d sélectionné</string>\n    <string name=\"logout_success\">Déconnexion réussie !</string>\n    <string name=\"dm_thread_info\">Informations</string>\n    <string name=\"mark_as_seen\">Marquer comme vu</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Écran d\\'accueil</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Afficher le clavier lors de la recherche</string>\n    <string name=\"pref_category_general\">Général</string>\n    <string name=\"pref_category_theme\">Thème</string>\n    <string name=\"pref_category_downloads\">Téléch.</string>\n    <string name=\"pref_category_locale\">Paramètres régionaux</string>\n    <string name=\"account\">Compte</string>\n    <string name=\"account_hint\">La connexion actuelle ne fonctionne pas ? Ajoutez simplement le compte à nouveau.</string>\n    <string name=\"add_account\">Ajouter un compte</string>\n    <string name=\"about_category_license\">Licence (en anglais seulement)</string>\n    <string name=\"about_documentation\">Visitez notre site Web</string>\n    <string name=\"about_documentation_summary\">Obtenez de l\\'aide, discutez, rencontrez d\\'autres personnes et amusez-vous !</string>\n    <string name=\"about_repository\">Voir notre code source sur GitHub</string>\n    <string name=\"about_repository_summary\">Audit, mettez une étoile, rapportez des bugs, contribuez et amusez-vous (encore)!</string>\n    <string name=\"about_feedback\">Envoyer vos retours par e-mail</string>\n    <string name=\"about_category_3pt\">Attributions tierces</string>\n    <string name=\"reminder\">Rappel</string>\n    <string name=\"reminder_summary\">Veuillez utiliser cette application de manière responsable. Les images téléchargées ne devraient être utilisées que pour les fins autorisées par les lois en rigueur.</string>\n    <string name=\"light_white_theme\">Blanc</string>\n    <string name=\"dark_black_theme\">Noir</string>\n    <string name=\"light_theme_settings\">Thème clair</string>\n    <string name=\"dark_theme_settings\">Thème sombre</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Ajouté aux Favoris !</string>\n    <string name=\"add_to_favorites\">Ajouter aux favoris</string>\n    <string name=\"accounts\">Comptes</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Lieux</string>\n    <string name=\"unknown\">Inconnu</string>\n    <string name=\"removed_from_favs\">Retiré des favoris !</string>\n    <string name=\"backup_and_restore\">Sauvegarder &amp; restaurer</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Sauvegardez les paramètres de l\\'application Barinsta, les données de connexion au compte et/ou les favoris dans un fichier de sauvegarde en texte clair ou crypté pour une restauration ultérieure.</string>\n    <string name=\"backup_warning\">Si vous sauvegardez les données de connexion de votre compte, traitez le fichier comme confidentiel et conservez-le dans un endroit sûr !</string>\n    <string name=\"create_backup\">Créer un nouveau fichier de sauvegarde</string>\n    <string name=\"restore_backup\">Restaurer à partir du fichier de sauvegarde existant</string>\n    <string name=\"file_chosen_label\">Fichier :</string>\n    <string name=\"enter_password\">Saisissez le mot de passe</string>\n    <string name=\"select_backup_file\">Sélectionnez un fichier de sauvegarde (.zaai/.backup)</string>\n    <string name=\"apply\">Appliquer</string>\n    <string name=\"save\">Sauvegarder</string>\n    <string name=\"caption\">Légende</string>\n    <string name=\"edit_caption\">Modifier la légende</string>\n    <string name=\"translate_caption\">Traduire la légende</string>\n    <string name=\"player_timeline_desc\">Chronologie du lecteur vidéo</string>\n    <string name=\"liking\">Entrain d\\'aimer…</string>\n    <string name=\"like_unsuccessful\">Erreur lors du \\\"j\\'aime\\\"</string>\n    <string name=\"unlike_unsuccessful\">Erreur lors du \\\"je n\\'aime plus\\\"</string>\n    <string name=\"unliking\">Entrain de ne plus aimer…</string>\n    <string name=\"controls\">Contrôles</string>\n    <string name=\"saving\">Entrain de sauvegarder…</string>\n    <string name=\"removing\">Entrain de supprimer…</string>\n    <string name=\"save_unsuccessful\">Erreur lors de l\\'enregistrement</string>\n    <string name=\"save_remove_unsuccessful\">Erreur lors de la supression</string>\n    <string name=\"downloading\">Entrain de télécharger…</string>\n    <string name=\"downloader_downloading_child\">Télécharge l\\'élément %1$d de %2$d</string>\n    <string name=\"delete\">Effacer</string>\n    <string name=\"comment\">Commentaire</string>\n    <string name=\"layout\">Disposition</string>\n    <string name=\"feed_stories\">Flux de stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Partager</string>\n    <string name=\"layout_style\">Style du modèle</string>\n    <string name=\"column_count\">Nombre de colonnes</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Afficher les noms</string>\n    <string name=\"show_avatars\">Afficher les avatars</string>\n    <string name=\"avatar_size\">Taille de l\\'avatar</string>\n    <string name=\"corners\">Coins</string>\n    <string name=\"show_grid_gap\">Afficher les lacunes de la grille</string>\n    <string name=\"post_not_found\">Publication non trouvée!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galerie</string>\n    <string name=\"camera\">Caméra</string>\n    <string name=\"all_photos\">Toutes les Photos</string>\n    <string name=\"all_media\">Tous les médias</string>\n    <string name=\"all_videos\">Toutes les vidéos</string>\n    <string name=\"brightness\">Luminosité</string>\n    <string name=\"contrast\">Contraste</string>\n    <string name=\"vibrance\">Couleurs vives</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Netteté</string>\n    <string name=\"exposure\">Exposition</string>\n    <string name=\"center\">Centrer</string>\n    <string name=\"color\">Couleur</string>\n    <string name=\"start\">Début</string>\n    <string name=\"end\">Fin</string>\n    <string name=\"bilateral_blur\">Flou bilatéral</string>\n    <string name=\"vignette\">Vignetage</string>\n    <string name=\"box_blur\">Flou de la boîte</string>\n    <string name=\"sepia\">Sépia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Réinitialiser</string>\n    <string name=\"crop\">Rogner</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d vue</item>\n        <item quantity=\"other\">%d vues</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">Détails</string>\n    <string name=\"title\">Titre</string>\n    <string name=\"members\">Membres</string>\n    <string name=\"admin\">Administrateur</string>\n    <string name=\"inviter\">Inviter</string>\n    <string name=\"mute_messages\">Mettre les messages en sourdine</string>\n    <string name=\"mute_mentions\">Désactiver les mentions</string>\n    <string name=\"add_members\">Ajouter des membres</string>\n    <string name=\"search\">Recherche</string>\n    <string name=\"done\">Effectuer</string>\n    <string name=\"dms_action_make_admin\">Rendre administrateur</string>\n    <string name=\"dms_action_remove_admin\">Supprimer l’administrateur</string>\n    <string name=\"edit_unsuccessful\">Échec de l\\'édition</string>\n    <string name=\"message\">Message</string>\n    <string name=\"tap_to_remove\">Appuyez pour supprimer</string>\n    <string name=\"forward\">Renvoyer</string>\n    <string name=\"forward_outgoing\">Vous avez renvoyer un message</string>\n    <string name=\"forward_incoming\">Message renvoyé</string>\n    <string name=\"add\">Ajouter</string>\n    <string name=\"send\">Envoyer</string>\n    <string name=\"replying_to_yourself\">Réponse à vous-même</string>\n    <string name=\"replying_to_user\">Répondre à %s</string>\n    <string name=\"replied_to_yourself\">Vous avez répondu à vous-même</string>\n    <string name=\"replied_you\">Vous avez répondu</string>\n    <string name=\"replied_you_group\">Vous avez répondu à %s</string>\n    <string name=\"replied_group\">Répondu à %s</string>\n    <string name=\"replied_to_you\">Vous a répondu</string>\n    <string name=\"replied_to_themself\">Répondu à eux-mêmes</string>\n    <string name=\"reacted_story_outgoing\">Vous avez réagi à sa story</string>\n    <string name=\"reacted_story_incoming\">A réagi à votre story</string>\n    <string name=\"mentioned_story_outgoing\">Vous les avez mentionnés dans votre story</string>\n    <string name=\"mentioned_story_incoming\">Vous a mentionné dans sa story</string>\n    <string name=\"replied_story_outgoing\">Vous avez répondu à sa story</string>\n    <string name=\"replied_story_incoming\">A répondu à votre story</string>\n    <string name=\"raven_image_expired\">Le message a expiré</string>\n    <string name=\"raven_image_info\">L\\'image expirera lorsqu\\'elle sera vue</string>\n    <string name=\"raven_video_expired\">La vidéo a expiré</string>\n    <string name=\"raven_video_info\">La vidéo expirera lorsqu\\'elle sera vue</string>\n    <string name=\"raven_msg_expired\">Le message est expiré</string>\n    <string name=\"raven_msg_info\">Le message expirera lorsqu\\'il sera vu</string>\n    <string name=\"story_share\">La story de %s</string>\n    <string name=\"story_share_highlight\">Mise en avant de la story de %s</string>\n    <string name=\"photo\">Photo</string>\n    <string name=\"video\">Vidéo</string>\n    <string name=\"voice_message\">Message vocal</string>\n    <string name=\"post\">Publication</string>\n    <string name=\"approval_required_for_new_members\">Approbation requise pour rejoindre</string>\n    <string name=\"requests\">Requêtes</string>\n    <string name=\"admins_only\">Administrateurs seulement</string>\n    <string name=\"added_by\">Ajouté par %s</string>\n    <string name=\"admin_approval_required\">Approbation de l\\'administrateur requise</string>\n    <string name=\"admin_approval_required_description\">Une approbation de l\\'administrateur sera nécessaire pour ajouter de nouveaux membres au groupe</string>\n    <string name=\"dms_action_end\">Terminer la conversation</string>\n    <string name=\"dms_action_end_question\">Terminer la conversation?</string>\n    <string name=\"dms_action_end_description\">Tous les membres seront retirés du groupe. Ils pourront toujours voir l\\'historique des discussions.</string>\n    <string name=\"pending_requests\">Demandes en attente</string>\n    <string name=\"accept_request_from_user\">Accepter la demande de %1s (%2s ) ?</string>\n    <string name=\"decline\">Refuser</string>\n    <string name=\"accept\">Accepter</string>\n    <string name=\"you\">Vous</string>\n    <string name=\"no_pending_requests\">Aucune demande en attente</string>\n    <string name=\"checking_for_new_messages\">Recherche de nouveaux messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">MP</string>\n    <string name=\"pref_category_notifications\">Notifications</string>\n    <string name=\"pref_category_post\">Publication</string>\n    <string name=\"enable_dm_notifications\">Activer les notifications pour les MPs</string>\n    <string name=\"enable_dm_auto_refesh\">Actualisation automatique des messages</string>\n    <string name=\"auto_refresh_every\">Actualisation automatique chaque</string>\n    <string name=\"secs\">secondes</string>\n    <string name=\"mins\">minutes</string>\n    <string name=\"search_giphy\">Rechercher GIPHY</string>\n    <string name=\"generic_null_response\">La réponse est nulle !</string>\n    <string name=\"generic_not_ok_response\">L\\'état de la réponse n\\'est pas correct !</string>\n    <string name=\"generic_failed_request\">La demande a échoué !</string>\n    <string name=\"hint_keyword\">Mot-clé</string>\n    <string name=\"toggle_keyword_filter\">Activer le filtre par mot-clé</string>\n    <string name=\"edit_keyword_filter\">Modifier les filtres de mots-clés</string>\n    <string name=\"added_keywords\">Mot-clé ajouté : %s à la liste de filtres</string>\n    <string name=\"removed_keywords\">Mot-clé supprimé : %s de la liste de filtres</string>\n    <string name=\"marked_as_seen\">Marqué comme vu</string>\n    <string name=\"delete_unsuccessful\">Suppression non réussie</string>\n    <string name=\"throttle_error\">Limité par Instagram en raison d\\'un trop grand nombre de requêtes API. Attendez un certain temps avant de réessayer.</string>\n    <string name=\"error\">Erreur</string>\n    <string name=\"account_logged_out\">Ce compte a été déconnecté.</string>\n    <string name=\"login_required\">Connexion requise!</string>\n    <string name=\"inactive_user\">L\\'utilisateur est inactif!</string>\n    <string name=\"crash_report_subject\">Rapport d\\'erreur de Barinsta</string>\n    <string name=\"crash_report_title\">Sélectionnez une application de messagerie pour envoyer les journaux d\\'erreurs</string>\n    <string name=\"not_found\">Aucun résultat!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Ignorer cette mise à jour</string>\n    <string name=\"on_latest_version\">Vous avez déjà la dernière version</string>\n    <string name=\"tab_order\">Ordre d\\'écran</string>\n    <string name=\"other_tabs\">Autres onglets</string>\n    <string name=\"tab_order_start_next_launch\">L\\'ordre des onglets sera reflété au prochain lancement</string>\n    <string name=\"dm_remove_warning\">Si enregistré, toutes les fonctionnalités liées aux DM seront désactivées au prochain lancement</string>\n    <string name=\"copy_caption\">Copie de la légende</string>\n    <string name=\"copy_reply\">Copie de la réponse</string>\n    <string name=\"restore\">Restaurer</string>\n    <string name=\"backup\">Sauvegarde</string>\n    <string name=\"dir_select_default_message\">Sélectionnez un dossier où Barinsta peut stocker les téléchargements et les fichiers temporaires.\\n\\nVous pouvez modifier cela plus tard dans Plus &gt; Paramètres &gt; Téléchargements.</string>\n    <string name=\"dir_select_reselect_message\">Android a modifié la façon dont les applications peuvent accéder aux fichiers et répertoires sur le stockage. Actuellement Barinsta n\\'a pas la permission d\\'accéder au dossier suivant:</string>\n    <string name=\"dir_select_permission_revoked_message\">Les autorisations pour le dossier précédemment sélectionné ont été révoquées par le système :</string>\n    <string name=\"dir_select_folder_not_exist\">Le dossier sélectionné précédemment n\\'existe pas :</string>\n    <string name=\"dir_select_message2\">Ré-sélectionnez le répertoire ou sélectionnez un nouveau répertoire en cliquant sur le bouton ci-dessous.</string>\n    <string name=\"select_a_folder\">Aucun dossier sélectionné!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Succès ! Veuillez patienter. Démarrage de l\\'application…</string>\n    <string name=\"barinsta_folder\">Dossier Barinsta</string>\n    <string name=\"top\">Retour vers le haut</string>\n    <string name=\"recent\">Récent</string>\n    <string name=\"clear\">Nettoyer</string>\n    <string name=\"no_external_map_app\">Aucune application de carte trouvée!</string>\n    <string name=\"click_to_show_full\">Cliquez pour voir le compte complet de j\\'aimes</string>\n    <string name=\"no_profile_pic_found\">Aucune photo de profil trouvée !</string>\n    <string name=\"swipe_up_confirmation\">Êtes-vous sûr de vouloir ouvrir ce lien ?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Partager via MP</string>\n    <string name=\"share_link\">Partager le lien…</string>\n    <string name=\"slide_to_cancel\">Glisser pour annuler</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hi/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>सिस्टम निर्धारित</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>सिस्टम के अनुसार</item>\n        <item>बैटरी स्तर के अनुसार</item>\n        <item>डार्क</item>\n        <item>हल्का</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>इंस्टाग्राम निर्धारित (पहले अपठित)</item>\n        <item>नए से पुराना</item>\n        <item>पुराने से नया</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>कुछ नहीं</item>\n        <item>\\@</item>\n        <item>पर</item>\n        <item>ऑन</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>सेकंड</item>\n        <item>मिनट</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">हमारे बारे में</string>\n    <string name=\"action_dms\">सीधा संदेश</string>\n    <string name=\"action_settings\">सेटिंग</string>\n    <string name=\"action_download\">डाउनलोड करें</string>\n    <string name=\"action_search\">ब्यबहारकारीयों के ब्यबहृत नाम ढुंढे...</string>\n    <string name=\"action_compare\">तुलना करें</string>\n    <string name=\"clipboard_error\">लेख कॉपी करने में त्रुटि</string>\n    <string name=\"clipboard_copied\">क्लिपबोर्ड में कॉपी हो गया है</string>\n    <string name=\"report\">रिपोर्ट करें</string>\n    <string name=\"set_password\">पासवर्ड के साथ फ़ाइल को सुरक्षित रखें</string>\n    <string name=\"password_no_max\">पासवर्ड</string>\n    <string name=\"ok\">ठीक है</string>\n    <string name=\"yes\">हाँ</string>\n    <string name=\"cancel\">रद्द करें</string>\n    <string name=\"no\">नहीं</string>\n    <string name=\"confirm\">पुष्टि करें</string>\n    <string name=\"title_favorites\">पसंदीदा</string>\n    <string name=\"title_discover\">खोजिए</string>\n    <string name=\"title_comments\">टिप्पणियाँ</string>\n    <string name=\"title_replies\">जवाब</string>\n    <string name=\"title_notifications\">गतिविधि</string>\n    <string name=\"update_check\">खुलने पर अपडेट के लिए जाँच करें</string>\n    <string name=\"flag_secure\">स्क्रीनशॉट को ब्लॉक करें &amp; ऐप प्रीव्यू</string>\n    <string name=\"download_user_folder\">पोस्ट को ब्यबहारकारी के नाम पर किये फोल्डरस में रखें</string>\n    <string name=\"download_prepend_username\">फ़ाइल के नाम के लिए यूजर का नाम डाल दे</string>\n    <string name=\"mark_as_seen_setting\">स्टोरि को दिखने के बाद \\\"दिखा गया\\\" दिखादें</string>\n    <string name=\"mark_as_seen_setting_summary\">सटोरि के लेखक जानेगा कि तुम देखे हो इसको</string>\n    <string name=\"hide_muted_reels_setting\">फ़ीड से म्यूट की गई स्टोरीस छिपाएं</string>\n    <string name=\"dm_mark_as_seen_setting\">तुम देखने के बाद सीधा संदेश को \\\"दिखागया\\\" लिखा जाएगा</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">अलग सदस्य जानेंगे कि तुम देखे हो</string>\n    <string name=\"autoplay_stories_setting\">व्हिडिओ स्टोरी ऑटो प्ले करे</string>\n    <string name=\"story_list_setting\">डिफॉल्ट स्टोरी लिस्ट</string>\n    <string name=\"story_list_setting_summary\">स्टोरी लिस्ट देखने के लिए</string>\n    <string name=\"activity_setting\">गतिविधि सूचनाएँ दिखायें</string>\n    <string name=\"story_sort_setting\">फ़ीड स्टोरीज क्रमबद्ध करें</string>\n    <string name=\"error_loading_profile\">प्रोफ़ाइल लोड करने में एरर! क्या यूजरनाम मान्य है? यदि हां, तो आप सीमित हो सकते हैं.</string>\n    <string name=\"error_loading_hashtag\">हैशटैग लोड करने में एरर! क्या नाम मान्य है?</string>\n    <string name=\"error_loading_location\">स्थान लोड करने में एरर! क्या यूआरएल मान्य है?</string>\n    <string name=\"error_creating_folders\">डाउनलोड के फोल्डर(स) बनाने में त्रुटि आ रहा है।</string>\n    <string name=\"select_folder\">फोल्डर का चयन करें</string>\n    <string name=\"theme_settings\">थीम</string>\n    <string name=\"select_language\">भाषा</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nपोस्ट</item>\n        <item quantity=\"other\">%s\\nPosts</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s पोस्ट</item>\n        <item quantity=\"other\">%s Posts</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nफोल्लोवेर</item>\n        <item quantity=\"other\">%s\\nFollowers</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nफोल्लोविंग</string>\n    <string name=\"post_viewer_autoplay_video\">वीडियो ऑटोप्ले करें</string>\n    <string name=\"post_viewer_background_play\">बैकग्राउंड में वीडियो जारी रखें</string>\n    <string name=\"post_viewer_background_play_summary\">ऐप के फोकस से बाहर होने पर वीडियो को पॉज न करें</string>\n    <string name=\"post_viewer_muted_autoplay\">सर्बदा वीडियो को शब्दहिन रखें</string>\n    <string name=\"post_viewer_show_captions\">हमेशा पोस्ट कैप्शन दिखाएं</string>\n    <string name=\"post_viewer_download_dialog_title\">डाउनलोड करने के लिए चयन करें</string>\n    <string name=\"post_viewer_download_current\">वर्तमान</string>\n    <string name=\"post_viewer_download_album\">सम्पूर्ण आलबम</string>\n    <string name=\"show_stories\">कहानियाँ दिखाएँ</string>\n    <string name=\"no_more_stories\">और कहानियाँ नहीं है!</string>\n    <string name=\"view_post\">पोस्ट देखें</string>\n    <string name=\"story_poll\">पोल</string>\n    <string name=\"answered_story\">सही उत्तर</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">आपके उत्तर: %s</string>\n    <string name=\"reply_story\">स्टोरि पे प्रतिक्रिया करें</string>\n    <string name=\"reply_hint\">जवाब…</string>\n    <string name=\"story_quiz\">प्रश्नोत्तरी</string>\n    <string name=\"story_slider\">स्लाइडर</string>\n    <string name=\"story_quizzed\">आप पहले से ही उत्तर दिये हो!</string>\n    <string name=\"story_mentions\">उल्लेख</string>\n    <string name=\"story_question\">प्रश्न</string>\n    <string name=\"priv_acc\">यह एकाउॅट निजी है</string>\n    <string name=\"priv_acc_confirm\">अनफलो चे बाद आप पोस्ट तक पहुंच नहिं पाओगे!\nआप निश्चित हैं?</string>\n    <string name=\"are_you_sure\">क्या आप सुनिश्चित हैं?</string>\n    <string name=\"no_acc\">आप लग इन और तरिकें के साथ कर सकते हो -&gt; दक्षिण के निचे कोने में एकाउॅट पर या आप सार्बजनीन एकाउॅट को बिना लग इन के देख सकते हो!</string>\n    <string name=\"empty_acc\">इसी एकाउॅट में कोई पोस्ट नहीं है।</string>\n    <string name=\"empty_list\">एसे कोई पोस्ट नहीं है!</string>\n    <string name=\"login\">लॉग इन करें</string>\n    <string name=\"logout\">लॉग आउट करें</string>\n    <string name=\"logout_summary\">इनस्टाग्राम में अज्ञात हो कर के देखें</string>\n    <string name=\"remove_all_acc\">समस्त खाता हटाएं</string>\n    <string name=\"remove_all_acc_warning\">अब एप से सब आड हुआ एकाउॅट मिट जाएगा!\\nसिर्फ एक एकाउॅट को मिटाने के लिये, एकाउॅट स्बिचर डाइलग से बही एकाउॅट को देर तक टैप करें।\\nआप आगे बढने के लिये चाहते हैं?</string>\n    <string name=\"time_settings\">दिनांक स्वरूप</string>\n    <string name=\"saved_create_collection\">नया संग्रह बनाएँ</string>\n    <string name=\"edit_collection\">संग्रह का नाम संपादित करें</string>\n    <string name=\"delete_collection\">संग्रह हटाएं</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">संग्रह में एड करे</string>\n    <string name=\"remove_from_collection\">संग्रह से निकालें</string>\n    <string name=\"liked\">पसंद किये</string>\n    <string name=\"saved\">सेव किया</string>\n    <string name=\"tagged\">टैग किये</string>\n    <string name=\"dm_person\">संदेश</string>\n    <string name=\"follow\">अनुसरण करे</string>\n    <string name=\"unfollow\">अनुसरण ना करें</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">पसंदीदा</string>\n    <string name=\"block\">ब्लॉक करें</string>\n    <string name=\"unblock\">अनब्ल़ॉक करें</string>\n    <string name=\"restrict\">प्रतिबंध लगाए</string>\n    <string name=\"unrestrict\">प्रतिबंध न लगाए</string>\n    <string name=\"mute_stories\">म्यूट स्टोरीज</string>\n    <string name=\"mute_posts\">म्यूट पोस्ट</string>\n    <string name=\"unmute_stories\">कहानियां अनम्यूट करें</string>\n    <string name=\"unmute_posts\">अनम्यूट पोस्ट</string>\n    <string name=\"remove_follower\">फॉलोवर को हटाएं</string>\n    <string name=\"bio_copy\">विवरण कॉपी करें</string>\n    <string name=\"bio_translate\">ट्रांसलेट बायो</string>\n    <string name=\"status_mutual\">आपसी</string>\n    <string name=\"status_following\">फोल्लोविंग</string>\n    <string name=\"status_follower\">फोल्लोवेर</string>\n    <string name=\"map\">नक्शा</string>\n    <string name=\"dialog_export_accounts\">खाते</string>\n    <string name=\"dialog_export_settings\">सेटिंग्स</string>\n    <string name=\"dialog_export_favorites\">पसंदीदा</string>\n    <string name=\"dialog_import_success\">सफलतापूर्वक इम्पोर्ट किया गया!</string>\n    <string name=\"dialog_import_failed\">आयात करने में विफल रहा!</string>\n    <string name=\"dialog_export_success\">सफलतापूर्वक निर्यात किया!</string>\n    <string name=\"dialog_export_failed\">निर्यात करने में विफल!</string>\n    <string name=\"refresh\">ताज़ा करें:</string>\n    <string name=\"get_cookies\">कुकिज पायें</string>\n    <string name=\"time_settings_title_custom\">कस्टम फर्माट ब्यबहार करें</string>\n    <string name=\"time_settings_title_separator\">सेपरेटर</string>\n    <string name=\"time_settings_title_time_format\">समय का स्बरुप</string>\n    <string name=\"time_settings_title_date_format\">दिनांक स्वरूप</string>\n    <string name=\"time_settings_title_preview\">अवलोकन</string>\n    <string name=\"time_settings_swap_time\">समय और दिबस अदला-बदली करें</string>\n    <string name=\"quick_access_cannot_delete_curr\">बर्तमीन ब्यबह्रत एकाउँट को मिटा नहिं सकते</string>\n    <string name=\"quick_access_confirm_delete\">क्या आप सचमुच %s मिटाना चाहते हैं?</string>\n    <string name=\"open_profile\">प्रोफाइल खुलें</string>\n    <string name=\"view_story\">कहानी देखें</string>\n    <string name=\"view_pfp\">प्रोफ़ाइल चित्र देखें</string>\n    <string name=\"dms_inbox_raven_message_unknown\">असमर्थित संदेश प्रकार</string>\n    <string name=\"dms_inbox_unsend\">सन्देश को प्रेरण न करें</string>\n    <string name=\"dms_inbox_giphy\">GIPHY. पर देखें</string>\n    <string name=\"dms_inbox_shared_post\">%s ने @%s. द्वारा एक पोस्ट शेयर की</string>\n    <string name=\"dms_inbox_shared_image\">%s ने एक फोटो शेयर की</string>\n    <string name=\"dms_inbox_shared_video\">%s ने एक वीडियो शेयर की</string>\n    <string name=\"dms_inbox_shared_message\">%s ने संदेश भेजा</string>\n    <string name=\"dms_inbox_shared_gif\">%s ने एक जीआईएफ शेयर किया</string>\n    <string name=\"dms_inbox_shared_sticker\">%s ने एक स्टिकर शेयर किया</string>\n    <string name=\"dms_inbox_shared_profile\">%s ने @%s. द्वारा एक प्रोफ़ाइल शेयर की</string>\n    <string name=\"dms_inbox_shared_location\">%s ने एक स्थान शेयर किया: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s ने @%s. द्वारा एक कहानी हाइलाइट शेयर की</string>\n    <string name=\"dms_inbox_shared_story\">%s ने @%s. द्वारा एक स्टोरी शेयर की</string>\n    <string name=\"dms_inbox_shared_voice\">%s ने व्हॉईस मेसेज भेजा</string>\n    <string name=\"dms_inbox_shared_clip\">%s ने @%s द्वारा एक क्लिप भेजी</string>\n    <string name=\"dms_inbox_shared_igtv\">%s ने @%s द्वारा एक IGTV व्हिडिओ भेजा</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">आपने %s की स्टोरी को रिप्लाय किया</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s ने आपकी स्टोरी को रिप्लाय किया %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">आपने %s की स्टोरी को रिएक्शन दी</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s ने आपकी स्टोरी को रिएक्शन दी: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">आपने @%s को अपनी स्टोरी मे मेनशन किया</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s ने आपको उनकी स्टोरी मे मेनशन किया</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>अज्ञात प्रकार के मिडीआ</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">मिडीया एक्सपायारड!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">वितरित</string>\n    <string name=\"dms_inbox_raven_media_sent\">भेजे गए</string>\n    <string name=\"dms_inbox_raven_media_opened\">खुला गया</string>\n    <string name=\"dms_inbox_raven_media_replayed\">फिर से खोलागया</string>\n    <string name=\"dms_inbox_raven_media_sending\">भेजा जा रहा है...</string>\n    <string name=\"dms_inbox_raven_media_blocked\">अवरोधित</string>\n    <string name=\"dms_inbox_raven_media_suggested\">सुझाव</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">स्क्रीनशॉट किया</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">पहुॅचा नहीं जा सकता</string>\n    <string name=\"dms_thread_message_hint\">मेसेज</string>\n    <string name=\"dms_thread_audio_hint\">ऑडिओ रिकॉर्ड करने के लिये बटन दबाये रखे</string>\n    <string name=\"dms_thread_updating\">अपडेट हो रहा है...</string>\n    <string name=\"dms_action_leave\">चॅट से बाहर निकले</string>\n    <string name=\"dms_action_leave_question\">इस बार्तालाप को छोड दें?</string>\n    <string name=\"dms_action_kick\">बाहर निकालें</string>\n    <string name=\"dms_left_users\">अवशेष ब्यबहारकारी</string>\n    <string name=\"dms_ERROR_INVALID_USER\">अवैध उपयोगकर्ता</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram DM के लिए 60 सेकंड से अधिक के वीडियो अपलोड करने की अनुमति नहीं देता है।</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram 60 सेकंड से अधिक समय तक ऑडियो अपलोड करने की अनुमति नहीं देता है।</string>\n    <string name=\"direct_download_loading\">पोस्ट(स्) ला रहे हैं</string>\n    <string name=\"downloader_complete\">डाउनलोड समाप्त हुआ</string>\n    <string name=\"downloader_preparing\">डाउनलोड करने की तैयारी...</string>\n    <string name=\"downloader_downloading_post\">डाउनलोड जारी है…</string>\n    <string name=\"downloader_downloading_media\">मिडीया डाउनलोड हो रहा है</string>\n    <string name=\"downloader_unknown_error\">अज्ञात त्रुटि आई !!!</string>\n    <string name=\"downloader_error_creating_folder\">फोलडर बनाने में त्रुटि!</string>\n    <string name=\"downloader_error_download_file\">फ़ाइल डाउनलोड करने में त्रुटि</string>\n    <string name=\"comment_viewer_translate_comment\">टिप्पणी का अनुवाद करें</string>\n    <string name=\"comment_viewer_delete_comment\">टिप्पणी हटाएं</string>\n    <string name=\"followers_type_followers\">अनुयायी</string>\n    <string name=\"followers_type_following\">आप फ़ॉलो कर रहे हैं</string>\n    <string name=\"followers_compare\">फलोअर्स &amp; फलोइंग के तुलना</string>\n    <string name=\"followers_both_following\">एक दुसरे को फोलो कर रहें हैं</string>\n    <string name=\"followers_not_following\">फोलो नहीं कर रहा %s</string>\n    <string name=\"followers_not_follower\">%s फोलो नहीं कर रहा है</string>\n    <string name=\"login_error_loading_cookies\">कुकीज लोड करने में त्रुटि</string>\n    <string name=\"comment_hint\">एक नया टिप्पणी लिखें...</string>\n    <string name=\"liked_notif\">आपकी पोस्ट को पसंद किया</string>\n    <string name=\"comment_notif\">आपके पोस्ट पर कमेंट किया है:</string>\n    <string name=\"follow_notif\">आपको फॉलो करना शुरू किया</string>\n    <string name=\"tagged_notif\">आपको एक पोस्ट में टैग किया गया है</string>\n    <string name=\"request_notif\">आपको फॉलो करने का निवेदन किया</string>\n    <string name=\"request_approve\">निवेदन को ग्रहण करें</string>\n    <string name=\"request_reject\">अनुरोध प्रत्याख्यान करें</string>\n    <string name=\"share_public_post\">इसी सार्वजनिक पोस्ट को भेजें...</string>\n    <string name=\"share_private_post\">यह एक निजी पोस्ट है! जो इसे देख सकते हैं उन्हें शेयर करें।</string>\n    <string name=\"discover_empty\">यह वर्ग रिक्त है...</string>\n    <string name=\"update_available\">नया संस्करण उपलब्ध है! (%s)</string>\n    <string name=\"updated\">बरिन्स्टा को अपडेट करने के लिए धन्यबाद!</string>\n    <string name=\"crash_title\">एप में त्रुटि हुयी।</string>\n    <string name=\"crash_descr\">Oops.. the app crashed, but don\\'t worry you can send error report to the developer to help him fix the issue. (:</string>\n    <string name=\"action_notif\">गतिविधि</string>\n    <string name=\"action_archive\">स्टोरी आरकाईव</string>\n    <string name=\"action_ayml\">सुझायें ऊपयोगकर्ता</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">आपके पास %d सूचनाएं हैं</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d अनुगामी</string>\n    <string name=\"activity_count_comments\">%d टिप्पणियाँ</string>\n    <string name=\"activity_count_commentlikes\">%d टिप्पणीयाँ पसन्दीत</string>\n    <string name=\"activity_count_usertags\">%d युजरटॅग</string>\n    <string name=\"activity_count_likes\">%d लाईक्स</string>\n    <string name=\"activity_count_poy\">आपकी %d तस्वीरें</string>\n    <string name=\"activity_count_requests\">%d फॉलो रिक्वेस्ट</string>\n    <string name=\"activity_notloggedin\">इस अधिसूचना पर क्लिक करने से पहले आपने लॉग आउट किया?!</string>\n    <string name=\"feed\">फ़ीड</string>\n    <string name=\"profile\">प्रोफाईल</string>\n    <string name=\"more\">अधिक</string>\n    <string name=\"title_dm\">डी एम</string>\n    <string name=\"number_selected\">%d सिलेक्ट किए हुए</string>\n    <string name=\"logout_success\">सफलतापूर्वक लॉग आउट कर दिया</string>\n    <string name=\"dm_thread_info\">जानकारी</string>\n    <string name=\"mark_as_seen\">पढ़ा गया के रूप मे मार्क करें</string>\n    <string name=\"version\">संस्करण</string>\n    <string name=\"pref_start_screen\">प्रारंभ पृष्ठ</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">सर्च मे कीबोर्ड दिखाएं</string>\n    <string name=\"pref_category_general\">सामान्य</string>\n    <string name=\"pref_category_theme\">थीम</string>\n    <string name=\"pref_category_downloads\">डाउनलोड</string>\n    <string name=\"pref_category_locale\">लोकल</string>\n    <string name=\"account\">खाता</string>\n    <string name=\"account_hint\">वर्तमान लॉगिन काम नहीं कर रहा है? बस फिर से खाता जोड़ें।</string>\n    <string name=\"add_account\">खाता जोड़ें</string>\n    <string name=\"about_category_license\">लायसेन्स (सिर्फ इंग्लिश)</string>\n    <string name=\"about_documentation\">हमारी वेबसाइट पर जाएं</string>\n    <string name=\"about_documentation_summary\">सपोर्ट करे, बात चीत करे, दुसरोसे मिले, आहे मजे किजिये!</string>\n    <string name=\"about_repository\">Github मे सोर्स कोड देखें</string>\n    <string name=\"about_repository_summary\">ऑडिट, स्टार, बुग रिपोर्ट करे, योगदान करे, ऑर मजे करे (फिर से)!</string>\n    <string name=\"about_feedback\">फीडबॅक ई मेल द्वारा भेजे</string>\n    <string name=\"about_category_3pt\">तीसरे पक्ष के अनुप्रयोग</string>\n    <string name=\"reminder\">रिमाइन्डर</string>\n    <string name=\"reminder_summary\">कृपया यह एप जिम्मेदारी से इस्तेमाल करे.\nडाउनलोड किए हुए इमेजेस सिर्फ नियम पूरक उद्देश से इस्तेमाल करे.</string>\n    <string name=\"light_white_theme\">सफेद</string>\n    <string name=\"dark_black_theme\">काला</string>\n    <string name=\"light_theme_settings\">हल्के रंग का थीम</string>\n    <string name=\"dark_theme_settings\">गहरे रंग की थीम</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">कॉफ़ी</string>\n    <string name=\"dark_material_dark_theme\">मैटेरियल डार्क</string>\n    <string name=\"added_to_favs\">फेवरेट में जोड़ा गया</string>\n    <string name=\"add_to_favorites\">फेवरेट के एड करे</string>\n    <string name=\"accounts\">खाते</string>\n    <string name=\"hashtags\">हैशटैग्स</string>\n    <string name=\"locations\">स्थान</string>\n    <string name=\"unknown\">अज्ञात</string>\n    <string name=\"removed_from_favs\">फेवरेट से हटाया</string>\n    <string name=\"backup_and_restore\">बैकअप &amp; रिस्टोर</string>\n    <string name=\"auto_backup\">ऑटो बैकअप</string>\n    <string name=\"auto_backup_summary\">एंड्रॉयड 6 के आगे, एंड्रॉयड का ऑटो बैकअप फीचर सब एप का बैकअप, अकाउंट लॉग इन डेटा, और फेवरेट गुगल ड्राइव पे होगा, जो एप वापिस इंस्टॉल करते वक्त रिस्टोर होगा अनइंस्टॉल करने के बाद.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">नया बैकअप बनाएँ</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">File:</string>\n    <string name=\"enter_password\">पासवर्ड दर्ज करें</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">लागू करें</string>\n    <string name=\"save\">सेव करें</string>\n    <string name=\"caption\">कैप्शन</string>\n    <string name=\"edit_caption\">कैप्शन का संपादन करें</string>\n    <string name=\"translate_caption\">Translate caption</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"liking\">Liking…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">Saving…</string>\n    <string name=\"removing\">Removing…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">डाउनलोड हो रहा है...</string>\n    <string name=\"downloader_downloading_child\">%2$d में से %1$d डाउनलोड हो रहा है</string>\n    <string name=\"delete\">हटाएँ</string>\n    <string name=\"comment\">टिप्पणी करें</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">पोस्ट खुल रहा है</string>\n    <string name=\"share\">सांझा करें</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Show names</string>\n    <string name=\"show_avatars\">Show avatars</string>\n    <string name=\"avatar_size\">Avatar size</string>\n    <string name=\"corners\">कोने</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">पोस्ट नहीं मिली</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">गैलरी</string>\n    <string name=\"camera\">कैमरा</string>\n    <string name=\"all_photos\">सभी तस्वीरें</string>\n    <string name=\"all_media\">All Media</string>\n    <string name=\"all_videos\">सभी वीडियो</string>\n    <string name=\"brightness\">Brightness</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exposure</string>\n    <string name=\"center\">मध्य</string>\n    <string name=\"color\">रंग</string>\n    <string name=\"start\">शुरू करें</string>\n    <string name=\"end\">समाप्त</string>\n    <string name=\"bilateral_blur\">बायलॅटरल ब्लर</string>\n    <string name=\"vignette\">विन्‍येट</string>\n    <string name=\"box_blur\">बॉक्स ब्लर</string>\n    <string name=\"sepia\">सिपिया</string>\n    <string name=\"clarendon\">क्लेरेंडन</string>\n    <string name=\"one977\">१९७७</string>\n    <string name=\"aden\">एडेन</string>\n    <string name=\"reset\">रीसेट</string>\n    <string name=\"crop\">क्रॉप</string>\n    <string name=\"normal\">सामान्य</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d व्यू</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s स्टोरीज</item>\n    </plurals>\n    <string name=\"details\">विवरण</string>\n    <string name=\"title\">शीर्षक:</string>\n    <string name=\"members\">सदस्य</string>\n    <string name=\"admin\">एडमिन</string>\n    <string name=\"inviter\">आमंत्रणकर्ता</string>\n    <string name=\"mute_messages\">मेसेज शांत रखे</string>\n    <string name=\"mute_mentions\">मेनशन शांत रखे</string>\n    <string name=\"add_members\">सदस्यों को जोड़ें</string>\n    <string name=\"search\">खोजें</string>\n    <string name=\"done\">ठीक</string>\n    <string name=\"dms_action_make_admin\">एडमिन बनाएं</string>\n    <string name=\"dms_action_remove_admin\">एडमिन से हटाएँ</string>\n    <string name=\"edit_unsuccessful\">एडिट असफल था</string>\n    <string name=\"message\">मेसेज</string>\n    <string name=\"tap_to_remove\">हटाने के लिए टैप करें</string>\n    <string name=\"forward\">फॉरवर्ड करें</string>\n    <string name=\"forward_outgoing\">आपने मेसेज फॉरवर्ड किया</string>\n    <string name=\"forward_incoming\">मेसेज फॉरवर्ड किया</string>\n    <string name=\"add\">जोड़ें</string>\n    <string name=\"send\">भेजें</string>\n    <string name=\"replying_to_yourself\">खुद को रिप्लाई कर रहे</string>\n    <string name=\"replying_to_user\">%s को रिप्लाई कर रहे</string>\n    <string name=\"replied_to_yourself\">खुद को रिप्लाई किया</string>\n    <string name=\"replied_you\">आपने रिप्लाई किया</string>\n    <string name=\"replied_you_group\">आपने %s को रिप्लाई किया</string>\n    <string name=\"replied_group\">%s को रिप्लाई किया</string>\n    <string name=\"replied_to_you\">आपको रिप्लाई किया</string>\n    <string name=\"replied_to_themself\">खुद को रिप्लाई किया</string>\n    <string name=\"reacted_story_outgoing\">आपने उनके स्टोरी को रिएक्ट किया</string>\n    <string name=\"reacted_story_incoming\">आपके स्टोरी को रिएक्ट किया</string>\n    <string name=\"mentioned_story_outgoing\">आपने @%s को अपनी स्टोरी मे मेनशन किया</string>\n    <string name=\"mentioned_story_incoming\">Mentioned you in their story</string>\n    <string name=\"replied_story_outgoing\">You replied to their story</string>\n    <string name=\"replied_story_incoming\">Replied to your story</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Photo</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Voice message</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">जुड़ने के लिए स्वीकृति आवश्यक है</string>\n    <string name=\"requests\">Requests</string>\n    <string name=\"admins_only\">केवल एडमिन</string>\n    <string name=\"added_by\">%s के द्वारा जोड़ा गया</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Decline</string>\n    <string name=\"accept\">Accept</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">संदेश</string>\n    <string name=\"pref_category_notifications\">सूचनाएं</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">DM सूचनाएं दिखायें</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">सेकंड</string>\n    <string name=\"mins\">मिनट</string>\n    <string name=\"search_giphy\">GIPHY खोजें</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">हटाने का कार्य विफल रहा</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">एरर</string>\n    <string name=\"account_logged_out\">यह अकाउंट लॉग आउट हुआ</string>\n    <string name=\"login_required\">लॉगिन अनिवार्य</string>\n    <string name=\"inactive_user\">उपयोगकर्ता निष्क्रिय है</string>\n    <string name=\"crash_report_subject\">Barinsta क्रैश रिपोर्ट</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">नहीं मिला!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">कैप्शन कॉपी करें</string>\n    <string name=\"copy_reply\">जवाब कॉपी करें</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-in/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Bawaan Sistem</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Otomatis / Ikuti Sistem</item>\n        <item>Otomatis / Ikuti Baterai</item>\n        <item>Gelap</item>\n        <item>Terang</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Bawaan Instagram (Belum terbaca lalu terbaca)</item>\n        <item>Terbaru ke terlama</item>\n        <item>Terlama ke terbaru</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Tidak ada</item>\n        <item>\\@</item>\n        <item>pada</item>\n        <item>pukul</item>\n        <item>tanggal</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>dtk</item>\n        <item>mnt</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Tentang</string>\n    <string name=\"action_dms\">Pesan</string>\n    <string name=\"action_settings\">Pengaturan</string>\n    <string name=\"action_download\">Unduh</string>\n    <string name=\"action_search\">Cari pengguna…</string>\n    <string name=\"action_compare\">Bandingkan</string>\n    <string name=\"clipboard_error\">Galat menyalin tulisan</string>\n    <string name=\"clipboard_copied\">Tersalin!</string>\n    <string name=\"report\">Laporkan</string>\n    <string name=\"set_password\">Lindungi berkas dengan sandi</string>\n    <string name=\"password_no_max\">Kata sandi</string>\n    <string name=\"ok\">Oke</string>\n    <string name=\"yes\">Ya</string>\n    <string name=\"cancel\">Batalkan</string>\n    <string name=\"no\">Tidak</string>\n    <string name=\"confirm\">Konfirmasi</string>\n    <string name=\"title_favorites\">Favorit</string>\n    <string name=\"title_discover\">Temukan</string>\n    <string name=\"title_comments\">Komentar</string>\n    <string name=\"title_replies\">Balasan</string>\n    <string name=\"title_notifications\">Aktivitas</string>\n    <string name=\"update_check\">Cek pembaruan saat memulai</string>\n    <string name=\"flag_secure\">Blokir tangkapan layar &amp; pratinjau aplikasi</string>\n    <string name=\"download_user_folder\">Unduh kiriman ke folder nama pengguna</string>\n    <string name=\"download_prepend_username\">Tambahkan nama pengguna sebelum nama berkas</string>\n    <string name=\"mark_as_seen_setting\">Tandai cerita dibaca setelah melihat</string>\n    <string name=\"mark_as_seen_setting_summary\">Pembuat cerita akan tahu Anda melihatnya</string>\n    <string name=\"hide_muted_reels_setting\">Sembunyikan cerita yang dibisukan</string>\n    <string name=\"dm_mark_as_seen_setting\">Tandai DM dibaca setelah melihat</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Peserta lain akan tahu Anda melihatnya</string>\n    <string name=\"autoplay_stories_setting\">Putar otomatis video cerita</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Nyalakan pemberitahuan aktivitas</string>\n    <string name=\"story_sort_setting\">Urutan umpan cerita</string>\n    <string name=\"error_loading_profile\">Galat memuat profil! Apakah nama penggunanya valid? Jika iya, maka kemungkinan akses Anda sedang dibatasi.</string>\n    <string name=\"error_loading_hashtag\">Galat memuat tagar! Apakah namanya valid?</string>\n    <string name=\"error_loading_location\">Galat memuat lokasi! Apakah URL-nya valid?</string>\n    <string name=\"error_creating_folders\">Galat saat membuat folder unduhan.</string>\n    <string name=\"select_folder\">Pilih folder</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Bahasa</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\nKiriman</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s Kiriman</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\npengikut</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\ndiikuti</string>\n    <string name=\"post_viewer_autoplay_video\">Otomatis putar video</string>\n    <string name=\"post_viewer_background_play\">Putar video di latar belakang</string>\n    <string name=\"post_viewer_background_play_summary\">Jangan hentikan video saat aplikasi tidak dalam fokus</string>\n    <string name=\"post_viewer_muted_autoplay\">Selalu bisukan video</string>\n    <string name=\"post_viewer_show_captions\">Selalu tampilkan keterangan kiriman</string>\n    <string name=\"post_viewer_download_dialog_title\">Pilih apa yang akan diunduh</string>\n    <string name=\"post_viewer_download_current\">Foto ini saja</string>\n    <string name=\"post_viewer_download_album\">Semua foto</string>\n    <string name=\"show_stories\">Tampilkan cerita</string>\n    <string name=\"no_more_stories\">Cerita sampai di sini!</string>\n    <string name=\"view_post\">Lihat Kiriman</string>\n    <string name=\"story_poll\">Jajak Pendapat</string>\n    <string name=\"answered_story\">Berhasil menjawab!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d tanggapan, rata-rata %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Tanggapan Anda: %s</string>\n    <string name=\"reply_story\">Balas cerita</string>\n    <string name=\"reply_hint\">Balas…</string>\n    <string name=\"story_quiz\">Kuis</string>\n    <string name=\"story_slider\">Slider</string>\n    <string name=\"story_quizzed\">Anda sudah menjawab!</string>\n    <string name=\"story_mentions\">Sebutan</string>\n    <string name=\"story_question\">Pertanyaan</string>\n    <string name=\"priv_acc\">Akun Ini Bersifat Pribadi</string>\n    <string name=\"priv_acc_confirm\">Anda tidak akan dapat mengakses kiriman setelah berhenti mengikuti! Anda yakin?</string>\n    <string name=\"are_you_sure\">Anda yakin?</string>\n    <string name=\"no_acc\">Anda bisa masuk melalui Lebih-&gt; Akun di pojok kanan bawah. Atau anda dapat melihat akun publik tanpa masuk!</string>\n    <string name=\"empty_acc\">Akun ini tidak memiliki kiriman</string>\n    <string name=\"empty_list\">Kiriman tidak ditemukan!</string>\n    <string name=\"login\">Masuk</string>\n    <string name=\"logout\">Keluar</string>\n    <string name=\"logout_summary\">Jelajahi Instagram secara anonim</string>\n    <string name=\"remove_all_acc\">Hapus semua akun</string>\n    <string name=\"remove_all_acc_warning\">Ini akan menghapus semua akun yang telah ditambahkan ke aplikasi!\\nUntuk menghapus satu akun, ketuk lama akun tersebut di dialog penukar akun.\\nApakah Anda ingin melanjutkan?</string>\n    <string name=\"time_settings\">Format tanggal</string>\n    <string name=\"saved_create_collection\">Buat koleksi baru</string>\n    <string name=\"edit_collection\">Sunting nama koleksi</string>\n    <string name=\"delete_collection\">Hapus koleksi</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Tambah ke koleksi...</string>\n    <string name=\"remove_from_collection\">Hapus dari koleksi</string>\n    <string name=\"liked\">Disuka</string>\n    <string name=\"saved\">Tersimpan</string>\n    <string name=\"tagged\">Ditandai</string>\n    <string name=\"dm_person\">Pesan</string>\n    <string name=\"follow\">Ikuti</string>\n    <string name=\"unfollow\">Batal Ikuti</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorit</string>\n    <string name=\"block\">Blokir</string>\n    <string name=\"unblock\">Batalkan Blokir</string>\n    <string name=\"restrict\">Batasi</string>\n    <string name=\"unrestrict\">Hilangkan Batasan</string>\n    <string name=\"mute_stories\">Bisukan cerita</string>\n    <string name=\"mute_posts\">Bisukan kiriman</string>\n    <string name=\"unmute_stories\">Batal bisukan cerita</string>\n    <string name=\"unmute_posts\">Batal bisukan kiriman</string>\n    <string name=\"remove_follower\">Hapus pengikut</string>\n    <string name=\"bio_copy\">Salin bio</string>\n    <string name=\"bio_translate\">Terjemahkan bio</string>\n    <string name=\"status_mutual\">Mutual</string>\n    <string name=\"status_following\">Mengikuti</string>\n    <string name=\"status_follower\">Pengikut</string>\n    <string name=\"map\">Peta</string>\n    <string name=\"dialog_export_accounts\">Akun</string>\n    <string name=\"dialog_export_settings\">Pengaturan</string>\n    <string name=\"dialog_export_favorites\">Kesukaan</string>\n    <string name=\"dialog_import_success\">Berhasil diimpor!</string>\n    <string name=\"dialog_import_failed\">Gagal mengimpor!</string>\n    <string name=\"dialog_export_success\">Berhasil diekspor!</string>\n    <string name=\"dialog_export_failed\">Gagal mengekspor!</string>\n    <string name=\"refresh\">Muat ulang</string>\n    <string name=\"get_cookies\">Dapatkan cookies</string>\n    <string name=\"time_settings_title_custom\">Gunakan format suaian</string>\n    <string name=\"time_settings_title_separator\">Pemisah</string>\n    <string name=\"time_settings_title_time_format\">Format Jam</string>\n    <string name=\"time_settings_title_date_format\">Format Tanggal</string>\n    <string name=\"time_settings_title_preview\">Pratinjau</string>\n    <string name=\"time_settings_swap_time\">Tukar posisi jam dan tanggal</string>\n    <string name=\"quick_access_cannot_delete_curr\">Tidak dapat menghapus akun yang sedang digunakan</string>\n    <string name=\"quick_access_confirm_delete\">Anda yakin ingin menghapus \\'%s\\'?</string>\n    <string name=\"open_profile\">Buka profil</string>\n    <string name=\"view_story\">Lihat cerita</string>\n    <string name=\"view_pfp\">Lihat foto profil</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Jenis pesan tidak didukung</string>\n    <string name=\"dms_inbox_unsend\">Batalkan pengiriman pesan</string>\n    <string name=\"dms_inbox_giphy\">Lihat di GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s berbagi kiriman @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s membagikan sebuah gambar</string>\n    <string name=\"dms_inbox_shared_video\">%s membagikan sebuah video</string>\n    <string name=\"dms_inbox_shared_message\">%s mengirimkan sebuah pesan</string>\n    <string name=\"dms_inbox_shared_gif\">%s membagikan sebuah gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s membagikan stiker</string>\n    <string name=\"dms_inbox_shared_profile\">%s membagikan profil @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s membagikan lokasi: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s membagikan sebuah cerita sorotan @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s membagikan cerita dari @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s mengirimkan pesan suara</string>\n    <string name=\"dms_inbox_shared_clip\">%s membagikan klip dari @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s membagikan video IGTV dari @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Anda membalas cerita mereka: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s membalas cerita Anda: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Reaksi Anda kepada cerita mereka: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">Reaksi %s terhadap cerita Anda: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Anda menyebut @%s dalam cerita Anda</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s menyebut Anda dalam cerita mereka</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Jenis media tidak diketahui</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media kedaluwarsa!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Diterima</string>\n    <string name=\"dms_inbox_raven_media_sent\">Terkirim</string>\n    <string name=\"dms_inbox_raven_media_opened\">Telah dibuka</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Telah dimainkan ulang</string>\n    <string name=\"dms_inbox_raven_media_sending\">Mengirim…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Telah diblokir</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Telah disarankan</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Layar tertangkap</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Tidak dapat mengirim</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Tekan dan tahan untuk merekam</string>\n    <string name=\"dms_thread_updating\">Memperbarui…</string>\n    <string name=\"dms_action_leave\">Tinggalkan obrolan</string>\n    <string name=\"dms_action_leave_question\">Tinggalkan obrolan ini?</string>\n    <string name=\"dms_action_kick\">Keluarkan</string>\n    <string name=\"dms_left_users\">Pengguna tersisa</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Pengguna tidak valid</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram tidak mengizinkan pengunggahan video lebih dari 60 detik untuk DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram tidak mengizinkan pengunggahan video lebih dari 60 detik.</string>\n    <string name=\"direct_download_loading\">Mendapatkan kiriman…</string>\n    <string name=\"downloader_complete\">Unduhan selesai</string>\n    <string name=\"downloader_preparing\">Mempersiapkan unduhan…</string>\n    <string name=\"downloader_downloading_post\">Mengunduh kiriman…</string>\n    <string name=\"downloader_downloading_media\">Mengunduh media</string>\n    <string name=\"downloader_unknown_error\">Galat tidak diketahui!</string>\n    <string name=\"downloader_error_creating_folder\">Galat membuat folder!</string>\n    <string name=\"downloader_error_download_file\">Galat mengunduh berkas</string>\n    <string name=\"comment_viewer_translate_comment\">Terjemahkan komentar</string>\n    <string name=\"comment_viewer_delete_comment\">Hapus komentar</string>\n    <string name=\"followers_type_followers\">Pengikut</string>\n    <string name=\"followers_type_following\">Diikuti</string>\n    <string name=\"followers_compare\">Membandingkan pengikut &amp; yang diikuti</string>\n    <string name=\"followers_both_following\">saling mengikuti</string>\n    <string name=\"followers_not_following\">tidak mengikuti %s</string>\n    <string name=\"followers_not_follower\">%s tidak mengikuti</string>\n    <string name=\"login_error_loading_cookies\">Galat memuat cookies</string>\n    <string name=\"comment_hint\">Tulis komentar baru…</string>\n    <string name=\"liked_notif\">Menyukai kiriman anda</string>\n    <string name=\"comment_notif\">Mengomentari kiriman anda:</string>\n    <string name=\"follow_notif\">Mulai mengikuti anda</string>\n    <string name=\"tagged_notif\">Menandai Anda pada sebuah kiriman</string>\n    <string name=\"request_notif\">Meminta mengikuti anda</string>\n    <string name=\"request_approve\">Terima permintaan</string>\n    <string name=\"request_reject\">Tolak permintaan</string>\n    <string name=\"share_public_post\">Bagikan kiriman publik ini ke…</string>\n    <string name=\"share_private_post\">Kiriman pribadi! Bagikan kepada orang yang bisa melihatnya.</string>\n    <string name=\"discover_empty\">Entah bagaimana kategori ini kosong…</string>\n    <string name=\"update_available\">Ada pembaruan! (%s)</string>\n    <string name=\"updated\">Terima kasih sudah menggunakan Barinsta!</string>\n    <string name=\"crash_title\">Aplikasi berhenti bekerja…</string>\n    <string name=\"crash_descr\">Ups! Aplikasi berhenti bekerja! Jangan khawatir, anda dapat mengirimkan laporan galat/kesalahan kepada Pengembang untuk membantunya memperbaiki masalah ini. (:</string>\n    <string name=\"action_notif\">Aktivitas</string>\n    <string name=\"action_archive\">Arsip cerita</string>\n    <string name=\"action_ayml\">Pengguna yang disarankan</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d mengikuti</string>\n    <string name=\"activity_count_comments\">%d komentar</string>\n    <string name=\"activity_count_commentlikes\">%d suka komentar</string>\n    <string name=\"activity_count_usertags\">%d sebutan pengguna</string>\n    <string name=\"activity_count_likes\">%d suka</string>\n    <string name=\"activity_count_poy\">%d foto untukmu</string>\n    <string name=\"activity_count_requests\">%d permintaan pengikut</string>\n    <string name=\"activity_notloggedin\">Anda keluar sebelum mengklik pemberitahuan ini?!</string>\n    <string name=\"feed\">Umpan</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Selengkapnya</string>\n    <string name=\"title_dm\">Pesan Langsung</string>\n    <string name=\"number_selected\">%d dipilih</string>\n    <string name=\"logout_success\">Berhasil keluar!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Tandai sudah dibaca</string>\n    <string name=\"version\">Versi</string>\n    <string name=\"pref_start_screen\">Layar mulai</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Tampilkan papan ketik pada pencarian</string>\n    <string name=\"pref_category_general\">Umum</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Unduhan</string>\n    <string name=\"pref_category_locale\">Lokal</string>\n    <string name=\"account\">Akun</string>\n    <string name=\"account_hint\">Login saat ini tidak bekerja? Tambahkan akun lagi. Simpel.</string>\n    <string name=\"add_account\">Tambah akun</string>\n    <string name=\"about_category_license\">Lisensi (Hanya bahasa Inggris)</string>\n    <string name=\"about_documentation\">Kunjungi situs web kami</string>\n    <string name=\"about_documentation_summary\">Dapatkan bantuan, diskusi, bertemu lainnya, dan bersenang-senang!</string>\n    <string name=\"about_repository\">Lihat kode sumber kami di GitHub</string>\n    <string name=\"about_repository_summary\">Audit, bintangi, laporan masalah, kontribusi, dan bersenang-senanglah (lagi)!</string>\n    <string name=\"about_feedback\">Kirim umpan balik melalui surel</string>\n    <string name=\"about_category_3pt\">Atribusi Pihak Ketiga</string>\n    <string name=\"reminder\">Pengingat</string>\n    <string name=\"reminder_summary\">Gunakan aplikasi ini dengan bertanggung jawab. Gambar yang diunduh seharusnya digunakan sesuai dengan batasan-batasan yang ditetapkan oleh Undang-Undang.</string>\n    <string name=\"light_white_theme\">Putih</string>\n    <string name=\"dark_black_theme\">Hitam</string>\n    <string name=\"light_theme_settings\">Tema terang</string>\n    <string name=\"dark_theme_settings\">Tema gelap</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Ditambahkan ke Favorit!</string>\n    <string name=\"add_to_favorites\">Tambahkan ke Favorit</string>\n    <string name=\"accounts\">Akun</string>\n    <string name=\"hashtags\">Tagar</string>\n    <string name=\"locations\">Lokasi</string>\n    <string name=\"unknown\">Tak diketahui</string>\n    <string name=\"removed_from_favs\">Dihapus dari Favorit!</string>\n    <string name=\"backup_and_restore\">Cadangan &amp; Pemulihan</string>\n    <string name=\"auto_backup\">Cadangkan otomatis</string>\n    <string name=\"auto_backup_summary\">Mulai Android 6, fitur Cadangkan Otomatis dari Android akan mengunggah seluruh pengaturan aplikasi, data masuk akun, dan favorit ke Google Drive Anda, yang dapat dipulihkan dengan memasang ulang aplikasi setelah pencopotan.</string>\n    <string name=\"auto_backup_warning\">Pengaturan ini tidak memiliki efek jika tidak ada Google Play Services, atau jika fitur Cadangkan Otomatis dinonaktifkan dari pengaturan perangkat. Mematikannya di sini tidak akan menghapus cadangan yang telah ada.</string>\n    <string name=\"auto_backup_setting\">Aktifkan pencadangan otomatis</string>\n    <string name=\"manual_backup\">Cadangan manual</string>\n    <string name=\"backup_summary\">Cadangkan pengaturan apl Barinsta, data login akun, dan/atau favorit ke berkas cadangan teks biasa atau terenkripsi untuk pemulihan nantinya.</string>\n    <string name=\"backup_warning\">Jika Anda mencadangkan data login akun, perlakukan sebagai berkas penting dan simpan di tempat yang aman!</string>\n    <string name=\"create_backup\">Buat berkas cadangan baru</string>\n    <string name=\"restore_backup\">Kembalikan dari berkas cadangan yang ada</string>\n    <string name=\"file_chosen_label\">Berkas:</string>\n    <string name=\"enter_password\">Masukkan sandi</string>\n    <string name=\"select_backup_file\">Pilih berkas cadangan (.zaai/.backup)</string>\n    <string name=\"apply\">Terapkan</string>\n    <string name=\"save\">Simpan</string>\n    <string name=\"caption\">Keterangan</string>\n    <string name=\"edit_caption\">Sunting keterangan</string>\n    <string name=\"translate_caption\">Terjemahkan keterangan</string>\n    <string name=\"player_timeline_desc\">Bilah waktu pemutar video</string>\n    <string name=\"liking\">Menyukai…</string>\n    <string name=\"like_unsuccessful\">Gagal menyukai</string>\n    <string name=\"unlike_unsuccessful\">Gagal membatalkan suka</string>\n    <string name=\"unliking\">Membatalkan suka…</string>\n    <string name=\"controls\">Kontrol</string>\n    <string name=\"saving\">Menyimpan…</string>\n    <string name=\"removing\">Menghpus…</string>\n    <string name=\"save_unsuccessful\">Gagal menyimpan</string>\n    <string name=\"save_remove_unsuccessful\">Gagal menghilangkan</string>\n    <string name=\"downloading\">Mengunduh…</string>\n    <string name=\"downloader_downloading_child\">Unduh item %1$d dari %2$d</string>\n    <string name=\"delete\">Hapus</string>\n    <string name=\"comment\">Komentar</string>\n    <string name=\"layout\">Tata letak</string>\n    <string name=\"feed_stories\">Umpan cerita</string>\n    <string name=\"opening_post\">Membuka kiriman…</string>\n    <string name=\"share\">Bagikan</string>\n    <string name=\"layout_style\">Gaya tata letak</string>\n    <string name=\"column_count\">Jumlah kolom</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Tampilkan nama</string>\n    <string name=\"show_avatars\">Tampilkan avatar</string>\n    <string name=\"avatar_size\">Ukuran avatar</string>\n    <string name=\"corners\">Sudut</string>\n    <string name=\"show_grid_gap\">Tampilkan batas kisi</string>\n    <string name=\"post_not_found\">Kiriman tidak ditemukan!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galeri</string>\n    <string name=\"camera\">Kamera</string>\n    <string name=\"all_photos\">Semua Foto</string>\n    <string name=\"all_media\">Semua Media</string>\n    <string name=\"all_videos\">Semua Video</string>\n    <string name=\"brightness\">Kecerahan</string>\n    <string name=\"contrast\">Kontras</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturasi</string>\n    <string name=\"sharpen\">Pertajam</string>\n    <string name=\"exposure\">Pajanan</string>\n    <string name=\"center\">Tengah</string>\n    <string name=\"color\">Warna</string>\n    <string name=\"start\">Mulai</string>\n    <string name=\"end\">Selesai</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Atur ulang</string>\n    <string name=\"crop\">Pangkas</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">%d dilihat</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s cerita</item>\n    </plurals>\n    <string name=\"details\">Detail</string>\n    <string name=\"title\">Judul</string>\n    <string name=\"members\">Anggota</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Pengundang</string>\n    <string name=\"mute_messages\">Bisukan pesan</string>\n    <string name=\"mute_mentions\">Bisukan sebutan</string>\n    <string name=\"add_members\">Tambah anggota</string>\n    <string name=\"search\">Cari</string>\n    <string name=\"done\">Selesai</string>\n    <string name=\"dms_action_make_admin\">Jadikan Admin</string>\n    <string name=\"dms_action_remove_admin\">Berhentikan sebagai Admin</string>\n    <string name=\"edit_unsuccessful\">Gagal menyunting</string>\n    <string name=\"message\">Pesan</string>\n    <string name=\"tap_to_remove\">Ketuk untuk hapus</string>\n    <string name=\"forward\">Teruskan</string>\n    <string name=\"forward_outgoing\">Anda meneruskan pesan</string>\n    <string name=\"forward_incoming\">Pesan diteruskan</string>\n    <string name=\"add\">Tambah</string>\n    <string name=\"send\">Kirim</string>\n    <string name=\"replying_to_yourself\">Membalas diri Anda sendiri</string>\n    <string name=\"replying_to_user\">Membalas ke %s</string>\n    <string name=\"replied_to_yourself\">Anda membalas diri Anda sendiri</string>\n    <string name=\"replied_you\">Anda membalas</string>\n    <string name=\"replied_you_group\">Anda membalas ke %s</string>\n    <string name=\"replied_group\">Membalas ke %s</string>\n    <string name=\"replied_to_you\">Membalas Anda</string>\n    <string name=\"replied_to_themself\">Membalas diri mereka sendiri</string>\n    <string name=\"reacted_story_outgoing\">Reaksi Anda kepada cerita mereka</string>\n    <string name=\"reacted_story_incoming\">Reaksi terhadap cerita Anda</string>\n    <string name=\"mentioned_story_outgoing\">Anda menyebut mereka dalam cerita Anda</string>\n    <string name=\"mentioned_story_incoming\">Menyebut Anda dalam cerita mereka</string>\n    <string name=\"replied_story_outgoing\">Anda membalas cerita mereka</string>\n    <string name=\"replied_story_incoming\">Membalas cerita Anda</string>\n    <string name=\"raven_image_expired\">Gambar telah kedaluwarsa</string>\n    <string name=\"raven_image_info\">Gambar akan kedaluwarsa setelah dilihat</string>\n    <string name=\"raven_video_expired\">Video telah kedaluwarsa</string>\n    <string name=\"raven_video_info\">Video akan kedaluwarsa setelah dilihat</string>\n    <string name=\"raven_msg_expired\">Pesan telah kedaluwarsa</string>\n    <string name=\"raven_msg_info\">Pesan akan kedaluwarsa setelah dilihat</string>\n    <string name=\"story_share\">Cerita @%s</string>\n    <string name=\"story_share_highlight\">Cerita sorotan @%s</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Pesan suara</string>\n    <string name=\"post\">Kiriman</string>\n    <string name=\"approval_required_for_new_members\">Membutuhkan persetujuan untuk bergabung</string>\n    <string name=\"requests\">Permintaan</string>\n    <string name=\"admins_only\">Hanya Admin</string>\n    <string name=\"added_by\">Ditambahkan oleh %s</string>\n    <string name=\"admin_approval_required\">Membutuhkan persetujuan Admin</string>\n    <string name=\"admin_approval_required_description\">Membutuhkan persetujuan Admin untuk menambahkan anggota baru ke dalam grup</string>\n    <string name=\"dms_action_end\">Akhiri obrolan</string>\n    <string name=\"dms_action_end_question\">Akhiri obrolan?</string>\n    <string name=\"dms_action_end_description\">Semua anggota akan dikeluarkan dari grup. Mereka tetap bisa melihat riwayat obrolan.</string>\n    <string name=\"pending_requests\">Permintaan Tertunda</string>\n    <string name=\"accept_request_from_user\">Terima permintaan dari %1s (%2s)?</string>\n    <string name=\"decline\">Tolak</string>\n    <string name=\"accept\">Terima</string>\n    <string name=\"you\">Anda</string>\n    <string name=\"no_pending_requests\">Tidak ada permintaan tertunda</string>\n    <string name=\"checking_for_new_messages\">Memerika pesan baru</string>\n    <string name=\"pref_category_stories\">Cerita</string>\n    <string name=\"pref_category_dm\">Pesan Langsung</string>\n    <string name=\"pref_category_notifications\">Pemberitahuan</string>\n    <string name=\"pref_category_post\">Kiriman</string>\n    <string name=\"enable_dm_notifications\">Aktifkan pemberitahuan pesan langsung</string>\n    <string name=\"enable_dm_auto_refesh\">Otomatis segarkan pesan</string>\n    <string name=\"auto_refresh_every\">Otomatis segarkan setiap</string>\n    <string name=\"secs\">dtk</string>\n    <string name=\"mins\">mnt</string>\n    <string name=\"search_giphy\">Cari GIPHY</string>\n    <string name=\"generic_null_response\">Respon null!</string>\n    <string name=\"generic_not_ok_response\">Status respons tidak ok!</string>\n    <string name=\"generic_failed_request\">Permintaan gagal!</string>\n    <string name=\"hint_keyword\">Kata kunci</string>\n    <string name=\"toggle_keyword_filter\">Aktifkan filter kata kunci</string>\n    <string name=\"edit_keyword_filter\">Sunting filter kata kunci</string>\n    <string name=\"added_keywords\">Menambahkan kata kunci: %s ke daftar filter</string>\n    <string name=\"removed_keywords\">Menghapus kata kunci: %s dari daftar filter</string>\n    <string name=\"marked_as_seen\">Tandai sudah dilihat</string>\n    <string name=\"delete_unsuccessful\">Gagal menghapus</string>\n    <string name=\"throttle_error\">Dibatasi oleh Instagram karena terlalu banyak permintaan API. Tunggu beberapa saat sebelum mencoba kembali.</string>\n    <string name=\"error\">Eror</string>\n    <string name=\"account_logged_out\">Akun ini sudah keluar.</string>\n    <string name=\"login_required\">Masuk dibutuhkan!</string>\n    <string name=\"inactive_user\">Pengguna tidak aktif!</string>\n    <string name=\"crash_report_subject\">Laporan Kerusakan Barinsta</string>\n    <string name=\"crash_report_title\">Pilih aplikasi surel untuk mengirim catatan kerusakan</string>\n    <string name=\"not_found\">Tidak ditermukan!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Lewatkan pembaruan</string>\n    <string name=\"on_latest_version\">Anda berada pada versi terbaru</string>\n    <string name=\"tab_order\">Urutan layar</string>\n    <string name=\"other_tabs\">Tab lainnya</string>\n    <string name=\"tab_order_start_next_launch\">Urutan tab akan tercermin pada peluncuran berikutnya</string>\n    <string name=\"dm_remove_warning\">Jika tersimpan, seluruh fitur berkenaan dengan Pesan Langsung akan dimatikan pada peluncuran berikutnya</string>\n    <string name=\"copy_caption\">Salin keterangan</string>\n    <string name=\"copy_reply\">Salin balasan</string>\n    <string name=\"restore\">Pulihkan</string>\n    <string name=\"backup\">Cadangkan</string>\n    <string name=\"dir_select_default_message\">Pilih folder di mana Barinsta akan menyimpan unduhan dan berkas-berkas sementara.\\n\\nAnda dapat mengubah ini nanti di Lanjutan &gt; Pengaturan &gt; Unduhan.</string>\n    <string name=\"dir_select_reselect_message\">Android telah mengubah cara aplikasi mengakses berkas dan direktori pada penyimpanan. Saat ini Barinsta tidak memiliki izin untuk mengakses folder berikut:</string>\n    <string name=\"dir_select_permission_revoked_message\">Izin untuk foler terpilih sebelumnya dicabut oleh sistem:</string>\n    <string name=\"dir_select_folder_not_exist\">Folder terpilih sebelumnya sekarang sudah tidak ada:</string>\n    <string name=\"dir_select_message2\">Pilih ulang direktori atau direktori baru dengan mengklik tombol di bawah ini.</string>\n    <string name=\"select_a_folder\">Tidak ada folder terpilih!</string>\n    <string name=\"dir_select_no_download_folder\">Mohon pilih direktori dari penyimpanan Anda, bukan kategori pada sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Berhasil! Tunggu sebentar. Memulia aplikasi…</string>\n    <string name=\"barinsta_folder\">Folder Barinsta</string>\n    <string name=\"top\">Teratas</string>\n    <string name=\"recent\">Terbaru</string>\n    <string name=\"clear\">Bersihkan</string>\n    <string name=\"no_external_map_app\">Aplikasi peta tidak ditemukan!</string>\n    <string name=\"click_to_show_full\">Klik untuk menampilkan jumlah suka seutuhnya</string>\n    <string name=\"no_profile_pic_found\">Foto profil tidak ditemukan!</string>\n    <string name=\"swipe_up_confirmation\">Apakah Anda yakin ingin membuka tautan ini?</string>\n    <string name=\"sending\">Mengirim…</string>\n    <string name=\"share_via_dm\">Bagikan lewat Pesan Langsung</string>\n    <string name=\"share_link\">Bagikan tautan…</string>\n    <string name=\"slide_to_cancel\">Geser untuk membatalkan</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-it/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Predefinito di Sistema</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automatico / Basato sul Sistema</item>\n        <item>Automatico / Basato sulla Batteria</item>\n        <item>Scuro</item>\n        <item>Chiaro</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram predefinito (non letto quindi leggi)</item>\n        <item>Dal più recente al più vecchio</item>\n        <item>Dal più vecchio al più recente</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Nessuno</item>\n        <item>\\@</item>\n        <item>a</item>\n        <item>il</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>secondi</item>\n        <item>minuti</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Info</string>\n    <string name=\"action_dms\">Messaggi Direct</string>\n    <string name=\"action_settings\">Impostazioni</string>\n    <string name=\"action_download\">Scarica</string>\n    <string name=\"action_search\">Cerca utente…</string>\n    <string name=\"action_compare\">Confronta</string>\n    <string name=\"clipboard_error\">Errore copiando il testo</string>\n    <string name=\"clipboard_copied\">Copiato negli appunti!</string>\n    <string name=\"report\">Segnala</string>\n    <string name=\"set_password\">Proteggi il file con password</string>\n    <string name=\"password_no_max\">Password</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Si</string>\n    <string name=\"cancel\">Annulla</string>\n    <string name=\"no\">No</string>\n    <string name=\"confirm\">Conferma</string>\n    <string name=\"title_favorites\">Preferiti</string>\n    <string name=\"title_discover\">Scopri</string>\n    <string name=\"title_comments\">Commenti</string>\n    <string name=\"title_replies\">Risposte</string>\n    <string name=\"title_notifications\">Attività</string>\n    <string name=\"update_check\">Verifica aggiornamenti all\\'avvio</string>\n    <string name=\"flag_secure\">Blocca screenshot &amp; anteprima app</string>\n    <string name=\"download_user_folder\">Scarica i post nelle cartelle del nome utente</string>\n    <string name=\"download_prepend_username\">Anteponi Nome Utente al Nome del File</string>\n    <string name=\"mark_as_seen_setting\">Segna le storie come viste dopo la visualizzazione</string>\n    <string name=\"mark_as_seen_setting_summary\">L\\'autore della storia saprà che l\\'hai visualizzata</string>\n    <string name=\"hide_muted_reels_setting\">Nascondi storie silenziate dal feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Segna il DM come visto dopo la visualizzazione</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Altri membri sapranno che lo hai visualizzato</string>\n    <string name=\"autoplay_stories_setting\">Riproduci automaticamente le storie video</string>\n    <string name=\"story_list_setting\">Mostra di default l\\'elenco delle storie</string>\n    <string name=\"story_list_setting_summary\">Per visualizzare le storie</string>\n    <string name=\"activity_setting\">Abilita notifiche attività</string>\n    <string name=\"story_sort_setting\">Ordinamento storie feed</string>\n    <string name=\"error_loading_profile\">Errore nel caricamento del profilo! Il nome utente è valido? Se sì, potresti essere considerato limitato.</string>\n    <string name=\"error_loading_hashtag\">Errore nel caricamento dell\\'hashtag! Il nome è valido?</string>\n    <string name=\"error_loading_location\">Errore nel caricamento della posizione! L\\'URL è valido?</string>\n    <string name=\"error_creating_folders\">Errore creando le cartelle di Download.</string>\n    <string name=\"select_folder\">Seleziona cartella</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Lingua</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPubblicazione</item>\n        <item quantity=\"other\">%s\\nPubblicazioni</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Post</item>\n        <item quantity=\"other\">%s Post</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSeguace</item>\n        <item quantity=\"other\">%s\\nSeguaci</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nSeguiti</string>\n    <string name=\"post_viewer_autoplay_video\">Riproduzione automatica video</string>\n    <string name=\"post_viewer_background_play\">Continua i video in sottofondo</string>\n    <string name=\"post_viewer_background_play_summary\">Non mettere in pausa i video quando l\\'applicazione è inattiva</string>\n    <string name=\"post_viewer_muted_autoplay\">Silenzia sempre i video</string>\n    <string name=\"post_viewer_show_captions\">Mostra sempre le didascalie dei post</string>\n    <string name=\"post_viewer_download_dialog_title\">Seleziona cosa scaricare</string>\n    <string name=\"post_viewer_download_current\">Corrente</string>\n    <string name=\"post_viewer_download_album\">Album Intero</string>\n    <string name=\"show_stories\">Mostra storie</string>\n    <string name=\"no_more_stories\">Nessun\\'altra storia!</string>\n    <string name=\"view_post\">Visualizza Post</string>\n    <string name=\"story_poll\">Sondaggio</string>\n    <string name=\"answered_story\">Risposto con successo!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d risposta media %s</item>\n        <item quantity=\"other\">%d risposte medie %s</item>\n    </plurals>\n    <string name=\"slider_answer\">La tua risposta: %s</string>\n    <string name=\"reply_story\">Rispondi alla storia</string>\n    <string name=\"reply_hint\">Rispondi…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Scorrimento</string>\n    <string name=\"story_quizzed\">Hai già risposto!</string>\n    <string name=\"story_mentions\">Menzioni</string>\n    <string name=\"story_question\">Domanda</string>\n    <string name=\"priv_acc\">Questo Profilo è Privato</string>\n    <string name=\"priv_acc_confirm\">Non potrai accederei ai post dopo aver smesso di seguire! Sei sicuro?</string>\n    <string name=\"are_you_sure\">Sei sicuro?</string>\n    <string name=\"no_acc\">Puoi accedere tramite Altro -&gt; Profilo nell\\'angolo in basso a destra o puoi visualizzare i profili pubblici senza accedere!</string>\n    <string name=\"empty_acc\">Questo Profilo Non ha Post</string>\n    <string name=\"empty_list\">Nessun Post Simile!</string>\n    <string name=\"login\">Accedi</string>\n    <string name=\"logout\">Disconettiti</string>\n    <string name=\"logout_summary\">Naviga su Instagram anonimamente</string>\n    <string name=\"remove_all_acc\">Rimuovi tutti i profili</string>\n    <string name=\"remove_all_acc_warning\">Questo rimuoverà tutti i profili aggiunti dall\\'app!\\nPer rimuovere solo un profilo, tieni premuto il profilo dalla finestra di cambio del profilo.\\nDesideri continuare?</string>\n    <string name=\"time_settings\">Formato della Data</string>\n    <string name=\"saved_create_collection\">Crea nuova raccolta</string>\n    <string name=\"edit_collection\">Modifica nome della raccolta</string>\n    <string name=\"delete_collection\">Elimina raccolta</string>\n    <string name=\"delete_collection_note\">Tutti i post contenuti nella raccolta cancellata rimarranno in altre raccolte.</string>\n    <string name=\"add_to_collection\">Aggiungi alla raccolta…</string>\n    <string name=\"remove_from_collection\">Rimuovi dalla raccolta</string>\n    <string name=\"liked\">Piaciuti</string>\n    <string name=\"saved\">Salvati</string>\n    <string name=\"tagged\">Taggati</string>\n    <string name=\"dm_person\">Messaggio</string>\n    <string name=\"follow\">Segui</string>\n    <string name=\"unfollow\">Non Seguire Più</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Preferito</string>\n    <string name=\"block\">Blocca</string>\n    <string name=\"unblock\">Sblocca</string>\n    <string name=\"restrict\">Limita</string>\n    <string name=\"unrestrict\">Rimuovi limitazione</string>\n    <string name=\"mute_stories\">Silenzia storie</string>\n    <string name=\"mute_posts\">Silenzia post</string>\n    <string name=\"unmute_stories\">Riattiva storie</string>\n    <string name=\"unmute_posts\">Riattiva i post</string>\n    <string name=\"remove_follower\">Rimuovi seguace</string>\n    <string name=\"bio_copy\">Copia bio</string>\n    <string name=\"bio_translate\">Traduci bio</string>\n    <string name=\"status_mutual\">Interesse reciproco</string>\n    <string name=\"status_following\">Seguendo</string>\n    <string name=\"status_follower\">Seguace</string>\n    <string name=\"map\">Mappa</string>\n    <string name=\"dialog_export_accounts\">Profili</string>\n    <string name=\"dialog_export_settings\">Impostazioni</string>\n    <string name=\"dialog_export_favorites\">Preferiti</string>\n    <string name=\"dialog_import_success\">Importato correttamente!</string>\n    <string name=\"dialog_import_failed\">Impossibile importare!</string>\n    <string name=\"dialog_export_success\">Esportato correttamente!</string>\n    <string name=\"dialog_export_failed\">Impossibile esportare!</string>\n    <string name=\"refresh\">Ricarica</string>\n    <string name=\"get_cookies\">Ottieni cookie</string>\n    <string name=\"time_settings_title_custom\">Usa formato personalizzato</string>\n    <string name=\"time_settings_title_separator\">Separatore</string>\n    <string name=\"time_settings_title_time_format\">Formato Ora</string>\n    <string name=\"time_settings_title_date_format\">Formato Data</string>\n    <string name=\"time_settings_title_preview\">Anteprima</string>\n    <string name=\"time_settings_swap_time\">Scambia posizioni Data e Ora</string>\n    <string name=\"quick_access_cannot_delete_curr\">Impossibile eliminare il profilo correntemente in uso</string>\n    <string name=\"quick_access_confirm_delete\">Sei sicuro di voler eliminare \\'%s\\'?</string>\n    <string name=\"open_profile\">Apri profilo</string>\n    <string name=\"view_story\">Visualizza storia</string>\n    <string name=\"view_pfp\">Vedi immagine del profilo</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Tipo di messaggio non supportato</string>\n    <string name=\"dms_inbox_unsend\">Annulla invio messaggio</string>\n    <string name=\"dms_inbox_giphy\">Visualizza su GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s ha condiviso un post di @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s ha condiviso un\\'immagine</string>\n    <string name=\"dms_inbox_shared_video\">%s ha condiviso un video</string>\n    <string name=\"dms_inbox_shared_message\">%s ha inviato un messaggio</string>\n    <string name=\"dms_inbox_shared_gif\">%s ha condiviso una gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s hai condiviso uno sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s ha condiviso un profilo: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s ha condiviso una posizione: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s ha condiviso una storia evidenziata di @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s ha condiviso una storia di @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s ha inviato un messaggio vocale</string>\n    <string name=\"dms_inbox_shared_clip\">%s ha condiviso un clip di @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s ha condiviso un video IGTV di @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Hai risposto alla sua storia: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s ha risposto alla tua storia: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Hai reagito alla sua storia: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s ha reagito alla tua storia: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Hai menzionato @%s nella tua storia</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s ti ha menzionato nella sua storia</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Tipo di media sconosciuto</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media scaduto!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Consegnato</string>\n    <string name=\"dms_inbox_raven_media_sent\">Inviato</string>\n    <string name=\"dms_inbox_raven_media_opened\">Aperto</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Riprodotto</string>\n    <string name=\"dms_inbox_raven_media_sending\">Inviando…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Bloccato</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggerito</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Catturato</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Impossibile consegnare</string>\n    <string name=\"dms_thread_message_hint\">Messaggio…</string>\n    <string name=\"dms_thread_audio_hint\">Tieni premuto per registrare l\\'audio</string>\n    <string name=\"dms_thread_updating\">Aggiornando…</string>\n    <string name=\"dms_action_leave\">Lascia chat</string>\n    <string name=\"dms_action_leave_question\">Lasciare questa chat?</string>\n    <string name=\"dms_action_kick\">Espelli</string>\n    <string name=\"dms_left_users\">Utenti rimanenti</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Utente non valido</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram non permette di caricare video più lunghi di 60 secondi per DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram non permette di caricare audio più lunghi di 60 secondi.</string>\n    <string name=\"direct_download_loading\">Recuperando i post</string>\n    <string name=\"downloader_complete\">Download completato</string>\n    <string name=\"downloader_preparing\">Preparazione al download…</string>\n    <string name=\"downloader_downloading_post\">Scaricando il post…</string>\n    <string name=\"downloader_downloading_media\">Download del media</string>\n    <string name=\"downloader_unknown_error\">Si è verificato un errore sconosciuto!!!</string>\n    <string name=\"downloader_error_creating_folder\">Errore creando la cartella!</string>\n    <string name=\"downloader_error_download_file\">Errore scaricando il file</string>\n    <string name=\"comment_viewer_translate_comment\">Traduci commento</string>\n    <string name=\"comment_viewer_delete_comment\">Elimina commento</string>\n    <string name=\"followers_type_followers\">Seguaci</string>\n    <string name=\"followers_type_following\">Seguiti</string>\n    <string name=\"followers_compare\">Confrontando seguaci e seguiti</string>\n    <string name=\"followers_both_following\">Si seguono a vicenda</string>\n    <string name=\"followers_not_following\">non segue %s</string>\n    <string name=\"followers_not_follower\">%s non ti segue</string>\n    <string name=\"login_error_loading_cookies\">Errore caricando i cookie</string>\n    <string name=\"comment_hint\">Scrivi un nuovo commento…</string>\n    <string name=\"liked_notif\">Ha messo Mi Piace al tuo post</string>\n    <string name=\"comment_notif\">Ha commentato il tuo post:</string>\n    <string name=\"follow_notif\">Ha iniziato a seguirti</string>\n    <string name=\"tagged_notif\">Ti ha taggato in un post</string>\n    <string name=\"request_notif\">Ha richiesto di seguirti</string>\n    <string name=\"request_approve\">Approva richiesta</string>\n    <string name=\"request_reject\">Rifiuta richiesta</string>\n    <string name=\"share_public_post\">Condividi questo post pubblico a…</string>\n    <string name=\"share_private_post\">Questo è un post privato! Condividi con coloro che possono vederlo.</string>\n    <string name=\"discover_empty\">Questa categoria è in qualche modo vuota…</string>\n    <string name=\"update_available\">Un aggiornamento è disponibile! (%s)</string>\n    <string name=\"updated\">Grazie per aver aggiornato Barinsta!</string>\n    <string name=\"crash_title\">L\\'app è crashata</string>\n    <string name=\"crash_descr\">Ops... l\\'app è crashata, ma non preoccuparti, puoi inviare una segnalazione dell\\'errore allo sviluppatore per aiutarlo a risolvere il problema. (:</string>\n    <string name=\"action_notif\">Attività</string>\n    <string name=\"action_archive\">Archivio storie</string>\n    <string name=\"action_ayml\">Utenti suggeriti</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">Hai %d notifica</item>\n        <item quantity=\"other\">Hai %d notifiche</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d seguaci</string>\n    <string name=\"activity_count_comments\">%d commenti</string>\n    <string name=\"activity_count_commentlikes\">%d mi piace al commento</string>\n    <string name=\"activity_count_usertags\">%d tag utente</string>\n    <string name=\"activity_count_likes\">%d mi piace</string>\n    <string name=\"activity_count_poy\">%d foto di te</string>\n    <string name=\"activity_count_requests\">%d richieste di seguito</string>\n    <string name=\"activity_notloggedin\">Ti sei disconnesso prima di cliccare questa notifica?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profilo</string>\n    <string name=\"more\">Altro</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d selezionati</string>\n    <string name=\"logout_success\">Disconnesso correttamente!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Segna come letto</string>\n    <string name=\"version\">Versione</string>\n    <string name=\"pref_start_screen\">Schermata iniziale</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Mostra la tastiera nella ricerca</string>\n    <string name=\"pref_category_general\">Generale</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Scaricati</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">Profilo</string>\n    <string name=\"account_hint\">L\\'accesso corrente non sta funzionando? Semplicemente, aggiungi di nuovo il profilo.</string>\n    <string name=\"add_account\">Aggiungi profilo</string>\n    <string name=\"about_category_license\">Licenza (solo inglese)</string>\n    <string name=\"about_documentation\">Visita il nostro sito web</string>\n    <string name=\"about_documentation_summary\">Ottieni supporto, discuti, incontra gli altri e divertiti!</string>\n    <string name=\"about_repository\">Visualizza il nostro codice sorgente su GitHub</string>\n    <string name=\"about_repository_summary\">Ispeziona, valuta, segnala bug, contribuisci, e divertiti (ancora)!</string>\n    <string name=\"about_feedback\">Invia feedback via email</string>\n    <string name=\"about_category_3pt\">Attribuzioni di Terze Parti</string>\n    <string name=\"reminder\">Promemoria</string>\n    <string name=\"reminder_summary\">Sei pregato di usare responsabilmente quest\\'app. Le immagini scaricate dovrebbero essere usate solo per scopi consentiti dalle leggi applicabili.</string>\n    <string name=\"light_white_theme\">Bianco</string>\n    <string name=\"dark_black_theme\">Nero</string>\n    <string name=\"light_theme_settings\">Tema chiaro</string>\n    <string name=\"dark_theme_settings\">Tema scuro</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Materiale Scuro</string>\n    <string name=\"added_to_favs\">Aggiunto ai Preferiti!</string>\n    <string name=\"add_to_favorites\">Aggiungi ai Preferiti</string>\n    <string name=\"accounts\">Profili</string>\n    <string name=\"hashtags\">Hashtag</string>\n    <string name=\"locations\">Luoghi</string>\n    <string name=\"unknown\">Sconosciuto</string>\n    <string name=\"removed_from_favs\">Rimosso dai Preferiti!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Ripristino</string>\n    <string name=\"auto_backup\">Backup automatico</string>\n    <string name=\"auto_backup_summary\">A partire da Android 6, la funzione di backup automatico di Android caricherà tutte le impostazioni dell\\'app, i dati di accesso dell\\'account, e preferiti sul tuo Google Drive, che può essere ripristinato reinstallando l\\'app dopo la disinstallazione.</string>\n    <string name=\"auto_backup_warning\">Questa preferenza non ha effetto se Google Play Services non è presente, o se il Backup Automatico è disabilitato dalle impostazioni del dispositivo. Disabilitare qui non cancella i backup esistenti.</string>\n    <string name=\"auto_backup_setting\">Attiva il backup automatico</string>\n    <string name=\"manual_backup\">Backup Manuale</string>\n    <string name=\"backup_summary\">Backup delle impostazioni dell\\'app Barinsta, dei dati di login dell\\'account e/o dei preferiti in un file di backup semplice o crittografato per il ripristino successivo.</string>\n    <string name=\"backup_warning\">Se stai effettuando il backup dei dati di accesso dell\\'account, tratta il file come confidenziale e tienilo in qualche posto sicuro!</string>\n    <string name=\"create_backup\">Crea un nuovo file di salvataggio</string>\n    <string name=\"restore_backup\">Ripristina da file di salvataggio esistente</string>\n    <string name=\"file_chosen_label\">File:</string>\n    <string name=\"enter_password\">Inserisci password</string>\n    <string name=\"select_backup_file\">Selezionare un file di backup (.zaai/.backup)</string>\n    <string name=\"apply\">Applica</string>\n    <string name=\"save\">Salva</string>\n    <string name=\"caption\">Didascalia</string>\n    <string name=\"edit_caption\">Modifica didascalia</string>\n    <string name=\"translate_caption\">Traduci didascalia</string>\n    <string name=\"player_timeline_desc\">Barra temporale del lettore video</string>\n    <string name=\"liking\">Mettendo Mi Piace…</string>\n    <string name=\"like_unsuccessful\">Impossibile mettere Mi Piace</string>\n    <string name=\"unlike_unsuccessful\">Impossibile rimuovere il Mi Piace</string>\n    <string name=\"unliking\">Rimuovendo il Mi Piace…</string>\n    <string name=\"controls\">Comandi</string>\n    <string name=\"saving\">Salvando…</string>\n    <string name=\"removing\">Rimuovendo…</string>\n    <string name=\"save_unsuccessful\">Salvataggio fallito</string>\n    <string name=\"save_remove_unsuccessful\">Rimozione fallita</string>\n    <string name=\"downloading\">Scaricando…</string>\n    <string name=\"downloader_downloading_child\">Scarica l\\'elemento %1$d di %2$d</string>\n    <string name=\"delete\">Elimina</string>\n    <string name=\"comment\">Commento</string>\n    <string name=\"layout\">Disposizione</string>\n    <string name=\"feed_stories\">Feed storie</string>\n    <string name=\"opening_post\">Aprendo il post…</string>\n    <string name=\"share\">Condividi</string>\n    <string name=\"layout_style\">Stile disposizione</string>\n    <string name=\"column_count\">Conteggio colonne</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Mostra nomi</string>\n    <string name=\"show_avatars\">Mostra avatar</string>\n    <string name=\"avatar_size\">Dimensione avatar</string>\n    <string name=\"corners\">Angoli</string>\n    <string name=\"show_grid_gap\">Mostra divario griglia</string>\n    <string name=\"post_not_found\">Post non trovato!</string>\n    <string name=\"no_external_app_url\">Nessuna app trovata per aprire gli URL</string>\n    <string name=\"gallery\">Galleria</string>\n    <string name=\"camera\">Fotocamera</string>\n    <string name=\"all_photos\">Tutte Le Foto</string>\n    <string name=\"all_media\">Tutti I Media</string>\n    <string name=\"all_videos\">Tutti I Video</string>\n    <string name=\"brightness\">Luminosità</string>\n    <string name=\"contrast\">Contrasto</string>\n    <string name=\"vibrance\">Vivacità</string>\n    <string name=\"saturation\">Saturazione</string>\n    <string name=\"sharpen\">Nitidizza</string>\n    <string name=\"exposure\">Esposizione</string>\n    <string name=\"center\">Centro</string>\n    <string name=\"color\">Colore</string>\n    <string name=\"start\">Inizio</string>\n    <string name=\"end\">Fine</string>\n    <string name=\"bilateral_blur\">Sfocatura Bilaterale</string>\n    <string name=\"vignette\">Vignetta</string>\n    <string name=\"box_blur\">Sfocatura casella</string>\n    <string name=\"sepia\">Seppiato</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Ripristina</string>\n    <string name=\"crop\">Ritaglia</string>\n    <string name=\"normal\">Normale</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d visualizzazione</item>\n        <item quantity=\"other\">%d visualizzazioni</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s storia</item>\n        <item quantity=\"other\">%s storie</item>\n    </plurals>\n    <string name=\"details\">Dettagli</string>\n    <string name=\"title\">Titolo</string>\n    <string name=\"members\">Membri</string>\n    <string name=\"admin\">Amministratore</string>\n    <string name=\"inviter\">Invito</string>\n    <string name=\"mute_messages\">Silenzia messaggi</string>\n    <string name=\"mute_mentions\">Menzioni mute</string>\n    <string name=\"add_members\">Aggiungi membri</string>\n    <string name=\"search\">Cerca</string>\n    <string name=\"done\">Fatto</string>\n    <string name=\"dms_action_make_admin\">Rendi amministratore</string>\n    <string name=\"dms_action_remove_admin\">Rimuovi come Amministratore</string>\n    <string name=\"edit_unsuccessful\">Modifica non riuscita</string>\n    <string name=\"message\">Messaggio</string>\n    <string name=\"tap_to_remove\">Tocca per rimuovere</string>\n    <string name=\"forward\">Inoltra</string>\n    <string name=\"forward_outgoing\">Hai inoltrato un messaggio</string>\n    <string name=\"forward_incoming\">Messaggio inoltrato</string>\n    <string name=\"add\">Aggiungi</string>\n    <string name=\"send\">Invia</string>\n    <string name=\"replying_to_yourself\">Rispondi a te stesso</string>\n    <string name=\"replying_to_user\">Rispondi a %s</string>\n    <string name=\"replied_to_yourself\">Hai risposto a te stesso</string>\n    <string name=\"replied_you\">Hai risposto</string>\n    <string name=\"replied_you_group\">Hai risposto a %s</string>\n    <string name=\"replied_group\">Risposto a %s</string>\n    <string name=\"replied_to_you\">Ti ha risposto</string>\n    <string name=\"replied_to_themself\">Risposto a loro stessi</string>\n    <string name=\"reacted_story_outgoing\">Avete reagito alla loro storia</string>\n    <string name=\"reacted_story_incoming\">Reagito alla tua storia</string>\n    <string name=\"mentioned_story_outgoing\">Li hai menzionati nella tua storia</string>\n    <string name=\"mentioned_story_incoming\">Ti ha menzionato nella loro storia</string>\n    <string name=\"replied_story_outgoing\">Hai risposto alla loro storia</string>\n    <string name=\"replied_story_incoming\">Ha risposto alla tua storia</string>\n    <string name=\"raven_image_expired\">L\\'immagine è scaduta</string>\n    <string name=\"raven_image_info\">L\\'immagine scadrà quando sarà vista</string>\n    <string name=\"raven_video_expired\">Il video è scaduto</string>\n    <string name=\"raven_video_info\">Il video scadrà quando visto</string>\n    <string name=\"raven_msg_expired\">Il messaggio è scaduto</string>\n    <string name=\"raven_msg_info\">Il messaggio scadrà quando visto</string>\n    <string name=\"story_share\">storia di @%s</string>\n    <string name=\"story_share_highlight\">storia in evidenza di @%s</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Messaggio vocale</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">Approva richiesta di adesione</string>\n    <string name=\"requests\">Richieste</string>\n    <string name=\"admins_only\">Solo Amministratori</string>\n    <string name=\"added_by\">Aggiunto da %s</string>\n    <string name=\"admin_approval_required\">Approvazione dell\\'amministratore richiesta</string>\n    <string name=\"admin_approval_required_description\">Per aggiungere nuovi membri al gruppo sarà necessaria l\\'approvazione dell\\'amministratore</string>\n    <string name=\"dms_action_end\">Termina chat</string>\n    <string name=\"dms_action_end_question\">Termina chat?</string>\n    <string name=\"dms_action_end_description\">Tutti i membri saranno rimossi dal gruppo. Essi saranno comunque in grado di visualizzare la cronologia della chat.</string>\n    <string name=\"pending_requests\">Richieste In Sospeso</string>\n    <string name=\"accept_request_from_user\">Accetta la richiesta da %1s (%2s)?</string>\n    <string name=\"decline\">Rifiuta</string>\n    <string name=\"accept\">Accetta</string>\n    <string name=\"you\">Tu</string>\n    <string name=\"no_pending_requests\">Nessuna richiesta in sospeso</string>\n    <string name=\"checking_for_new_messages\">Controllando per nuovi messaggi</string>\n    <string name=\"pref_category_stories\">Storie</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Notifiche</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">Abilita notifiche DM</string>\n    <string name=\"enable_dm_auto_refesh\">Aggiornamento automatico dei messaggi</string>\n    <string name=\"auto_refresh_every\">Aggiornamento automatico ogni</string>\n    <string name=\"secs\">secondi</string>\n    <string name=\"mins\">minuti</string>\n    <string name=\"search_giphy\">Cerca GIPHY</string>\n    <string name=\"generic_null_response\">La risposta è nulla!</string>\n    <string name=\"generic_not_ok_response\">Lo stato di risposta non è ok!</string>\n    <string name=\"generic_failed_request\">Richiesta fallita!</string>\n    <string name=\"hint_keyword\">Parola Chiave</string>\n    <string name=\"toggle_keyword_filter\">Abilita filtro parole chiave</string>\n    <string name=\"edit_keyword_filter\">Modifica filtri di parole chiave</string>\n    <string name=\"added_keywords\">Parola chiave aggiunta: %s alla lista filtri</string>\n    <string name=\"removed_keywords\">Parola chiave rimossa: %s dalla lista filtri</string>\n    <string name=\"marked_as_seen\">Segnato come visto</string>\n    <string name=\"delete_unsuccessful\">Eliminazione non riuscita</string>\n    <string name=\"throttle_error\">Limitato da Instagram a causa delle troppe richieste API. Aspetta un po\\' prima di riprovare.</string>\n    <string name=\"error\">Errore</string>\n    <string name=\"account_logged_out\">Questo account è stato disconnesso.</string>\n    <string name=\"login_required\">Login richiesto!</string>\n    <string name=\"inactive_user\">Utente è inattivo!</string>\n    <string name=\"crash_report_subject\">Rapporto sugli errori di Barinsta</string>\n    <string name=\"crash_report_title\">Selezionare un\\'applicazione di posta elettronica per inviare i registri di errori</string>\n    <string name=\"not_found\">Non trovato!</string>\n    <string name=\"rate_limit\">L\\'intervallo del tuo IP è stato limitato da Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Scopri di più.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Salta questo aggiornamento</string>\n    <string name=\"on_latest_version\">Hai già l\\'ultima versione</string>\n    <string name=\"tab_order\">Ordine schermata</string>\n    <string name=\"other_tabs\">Altre schede</string>\n    <string name=\"tab_order_start_next_launch\">L\\'ordine delle schede sarà rifletto al prossimo avvio</string>\n    <string name=\"dm_remove_warning\">Se salvate, tutte le funzionalità correlate a DM saranno disabilitate al prossimo avvio</string>\n    <string name=\"copy_caption\">Copia didascalia</string>\n    <string name=\"copy_reply\">Copia risposta</string>\n    <string name=\"restore\">Ripristina</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Seleziona una cartella in cui Barinsta può memorizzare i download e i file temporanei.\\n\\nPuoi modificarla in seguito in Altro &gt; Impostazioni &gt; Scaricati.</string>\n    <string name=\"dir_select_reselect_message\">Android ha cambiato il modo in cui le app possono accedere a file e cartelle nell\\'archiviazione. Attualmente Barinsta non ha il permesso di accedere alla seguente cartella:</string>\n    <string name=\"dir_select_permission_revoked_message\">I permessi per la cartella precedentemente selezionata sono stati revocati dal sistema:</string>\n    <string name=\"dir_select_folder_not_exist\">La cartella precedentemente selezionata non esiste ora:</string>\n    <string name=\"dir_select_message2\">Riselezionare la cartella o selezionare una nuova cartella facendo clic sul pulsante qui sotto.</string>\n    <string name=\"select_a_folder\">Nessuna cartella selezionata!</string>\n    <string name=\"dir_select_no_download_folder\">Scegli una cartella dal tuo archivio, non una categoria sulla barra laterale.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Successo! Attendere prego. Avvio app…</string>\n    <string name=\"barinsta_folder\">Cartella di Barinsta</string>\n    <string name=\"top\">Torna su</string>\n    <string name=\"recent\">Recente</string>\n    <string name=\"clear\">Pulisci</string>\n    <string name=\"no_external_map_app\">Nessuna app mappa trovata!</string>\n    <string name=\"click_to_show_full\">Clicca per mostrare il conteggio completo</string>\n    <string name=\"no_profile_pic_found\">Nessuna foto profilo trovata!</string>\n    <string name=\"swipe_up_confirmation\">Sei sicuro di voler aprire questo link?</string>\n    <string name=\"sending\">Inviando…</string>\n    <string name=\"share_via_dm\">Condividi tramite DM</string>\n    <string name=\"share_link\">Condivisione link…</string>\n    <string name=\"slide_to_cancel\">Trascinare per cancellare</string>\n    <string name=\"disable_screen_transitions\">Disabilita transizioni schermo</string>\n    <string name=\"invalid_format\">Formato non valido</string>\n    <string name=\"no_directory_picker_activity\">Barinsta non può avviare il file manager di Android. Assicurati che sia installato e abilitato sul tuo dispositivo.</string>\n    <string name=\"story_stickers\">Adesivi</string>\n    <string name=\"story_list\">Elenco storie</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>システムのデフォルト</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>自動/システム設定に従う</item>\n        <item>自動/バッテリー設定に従う</item>\n        <item>ダーク</item>\n        <item>ライト</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagramのデフォルト(未読を上位に)</item>\n        <item>最新を上位に</item>\n        <item>古いものを上位に</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>なし</item>\n        <item>\\@</item>\n        <item></item>\n        <item></item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>秒</item>\n        <item>分</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">このアプリについて</string>\n    <string name=\"action_dms\">ダイレクトメッセージ</string>\n    <string name=\"action_settings\">設定</string>\n    <string name=\"action_download\">ダウンロード</string>\n    <string name=\"action_search\">ユーザー名を検索…</string>\n    <string name=\"action_compare\">比較する</string>\n    <string name=\"clipboard_error\">テキストのコピーに失敗しました</string>\n    <string name=\"clipboard_copied\">クリップボードにコピーしました！</string>\n    <string name=\"report\">報告する</string>\n    <string name=\"set_password\">パスワードでファイルを保護する</string>\n    <string name=\"password_no_max\">パスワード</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">はい</string>\n    <string name=\"cancel\">キャンセル</string>\n    <string name=\"no\">いいえ</string>\n    <string name=\"confirm\">確認</string>\n    <string name=\"title_favorites\">お気に入り</string>\n    <string name=\"title_discover\">発見</string>\n    <string name=\"title_comments\">コメント</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">アクティビティ</string>\n    <string name=\"update_check\">起動時にアップデートを確認</string>\n    <string name=\"flag_secure\">スクリーンショットとアプリプレビューをブロック</string>\n    <string name=\"download_user_folder\">ユーザ名のフォルダに投稿をダウンロード</string>\n    <string name=\"download_prepend_username\">ファイル名の先頭にユーザー名を追加</string>\n    <string name=\"mark_as_seen_setting\">ストーリーズを表示後に既読にする</string>\n    <string name=\"mark_as_seen_setting_summary\">ストーリーの作成者は、あなたが閲覧したことを知ることができます。</string>\n    <string name=\"hide_muted_reels_setting\">ミュートされたストーリーをフィードから隠す</string>\n    <string name=\"dm_mark_as_seen_setting\">DMを表示後に既読にする</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">ほかのメンバーは、あなたが閲覧したことを知ることができます。</string>\n    <string name=\"autoplay_stories_setting\">ビデオストーリーズを自動再生</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">アクティビティの通知を有効化</string>\n    <string name=\"story_sort_setting\">フィードのストーリーズの並び順</string>\n    <string name=\"error_loading_profile\">プロフィールのロードでエラーが発生しました。ユーザーネームは正しいですか？接続制限されているかもしれません。</string>\n    <string name=\"error_loading_hashtag\">ハッシュタグのロードでエラーが発生しました。名前は正しいですか？</string>\n    <string name=\"error_loading_location\">場所のロードでエラーが発生しました。URLは正しいですか？</string>\n    <string name=\"error_creating_folders\">ダウンロードフォルダの作成中にエラーが発生しました。</string>\n    <string name=\"select_folder\">フォルダーを選択</string>\n    <string name=\"theme_settings\">テーマ</string>\n    <string name=\"select_language\">言語</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\n件</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s 件</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\nフォロワー</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nフォロー中</string>\n    <string name=\"post_viewer_autoplay_video\">動画を自動再生する</string>\n    <string name=\"post_viewer_background_play\">バックグラウンドでビデオの再生を続ける</string>\n    <string name=\"post_viewer_background_play_summary\">アプリがフォーカスされていないときにビデオを一時停止しない</string>\n    <string name=\"post_viewer_muted_autoplay\">動画を常にミュートする</string>\n    <string name=\"post_viewer_show_captions\">キャプションを常に表示</string>\n    <string name=\"post_viewer_download_dialog_title\">ダウンロード対象を選択</string>\n    <string name=\"post_viewer_download_current\">この画像</string>\n    <string name=\"post_viewer_download_album\">アルバム全体</string>\n    <string name=\"show_stories\">ストーリーズを表示</string>\n    <string name=\"no_more_stories\">これ以上のストーリーズはありません！</string>\n    <string name=\"view_post\">投稿を表示</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">回答しました！</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">あなたの回答: %s</string>\n    <string name=\"reply_story\">ストーリーズに返信</string>\n    <string name=\"reply_hint\">返信…</string>\n    <string name=\"story_quiz\">質問</string>\n    <string name=\"story_slider\">スライダー</string>\n    <string name=\"story_quizzed\">すでに回答済みです！</string>\n    <string name=\"story_mentions\">メンション</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">このアカウントは非公開です</string>\n    <string name=\"priv_acc_confirm\">フォローを解除すると、投稿にアクセスできなくなります。よろしいですか？</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">右下にある 詳細 -&gt; アカウント でログインすることができます。または、ログインせずに公開アカウントを表示することができます！</string>\n    <string name=\"empty_acc\">このアカウントには投稿がありません</string>\n    <string name=\"empty_list\">投稿はありません！</string>\n    <string name=\"login\">ログイン</string>\n    <string name=\"logout\">ログアウト</string>\n    <string name=\"logout_summary\">Instagramを匿名で閲覧する</string>\n    <string name=\"remove_all_acc\">すべてのアカウントを削除</string>\n    <string name=\"remove_all_acc_warning\">追加された全てのアカウントがアプリから削除されます！\\nアカウントを1つだけ削除するには、アカウント選択ダイアログからアカウントを長押ししてください \\n\\n続けますか?</string>\n    <string name=\"time_settings\">日付の形式</string>\n    <string name=\"saved_create_collection\">新しいコレクションを作成</string>\n    <string name=\"edit_collection\">コレクションの名前を編集</string>\n    <string name=\"delete_collection\">コレクションを削除する</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">コレクションから削除する</string>\n    <string name=\"liked\">いいね！</string>\n    <string name=\"saved\">保存</string>\n    <string name=\"tagged\">タグ付き</string>\n    <string name=\"dm_person\">メッセージ</string>\n    <string name=\"follow\">フォローする</string>\n    <string name=\"unfollow\">フォローを解除</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">お気に入り</string>\n    <string name=\"block\">ブロック</string>\n    <string name=\"unblock\">ブロックを解除</string>\n    <string name=\"restrict\">制限する</string>\n    <string name=\"unrestrict\">制限を解除</string>\n    <string name=\"mute_stories\">ストーリーズをミュート</string>\n    <string name=\"mute_posts\">投稿をミュート</string>\n    <string name=\"unmute_stories\">ストーリーズのミュートを解除</string>\n    <string name=\"unmute_posts\">投稿のミュートを解除</string>\n    <string name=\"remove_follower\">フォロワーを削除する</string>\n    <string name=\"bio_copy\">プロフィールをコピー</string>\n    <string name=\"bio_translate\">プロフィールを翻訳する</string>\n    <string name=\"status_mutual\">相互フォロー</string>\n    <string name=\"status_following\">フォロー中</string>\n    <string name=\"status_follower\">フォロワー</string>\n    <string name=\"map\">マップ</string>\n    <string name=\"dialog_export_accounts\">アカウント</string>\n    <string name=\"dialog_export_settings\">設定</string>\n    <string name=\"dialog_export_favorites\">お気に入り</string>\n    <string name=\"dialog_import_success\">インポートに成功しました！</string>\n    <string name=\"dialog_import_failed\">インポートに失敗しました！</string>\n    <string name=\"dialog_export_success\">エクスポートに成功しました！</string>\n    <string name=\"dialog_export_failed\">エクスポートに失敗しました！</string>\n    <string name=\"refresh\">更新</string>\n    <string name=\"get_cookies\">Cookie を取得する</string>\n    <string name=\"time_settings_title_custom\">カスタム形式を使用する</string>\n    <string name=\"time_settings_title_separator\">区切り記号</string>\n    <string name=\"time_settings_title_time_format\">時刻形式</string>\n    <string name=\"time_settings_title_date_format\">日付形式</string>\n    <string name=\"time_settings_title_preview\">プレビュー</string>\n    <string name=\"time_settings_swap_time\">時刻と日付の位置を入れ替え</string>\n    <string name=\"quick_access_cannot_delete_curr\">現在使用中のアカウントは削除できません</string>\n    <string name=\"quick_access_confirm_delete\">\\'%s\\' を削除してもよろしいですか？</string>\n    <string name=\"open_profile\">プロフィールを開く</string>\n    <string name=\"view_story\">ストーリーズを表示</string>\n    <string name=\"view_pfp\">プロフィール画像を表示</string>\n    <string name=\"dms_inbox_raven_message_unknown\">サポートされていないメッセージ形式です</string>\n    <string name=\"dms_inbox_unsend\">メッセージの送信を取り消す</string>\n    <string name=\"dms_inbox_giphy\">GIPHYで見る</string>\n    <string name=\"dms_inbox_shared_post\">%s が @%s の投稿を共有しました</string>\n    <string name=\"dms_inbox_shared_image\">%s が画像を共有しました</string>\n    <string name=\"dms_inbox_shared_video\">%s がビデオを共有しました</string>\n    <string name=\"dms_inbox_shared_message\">%s がメッセージを送信しました</string>\n    <string name=\"dms_inbox_shared_gif\">%s がgifを共有しました</string>\n    <string name=\"dms_inbox_shared_sticker\">%s がステッカーを共有しました</string>\n    <string name=\"dms_inbox_shared_profile\">%s がプロフィールを共有しました: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s が場所を共有しました: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s が @%s のストーリーのハイライトを共有しました</string>\n    <string name=\"dms_inbox_shared_story\">%s が @%s のストーリーを共有しました</string>\n    <string name=\"dms_inbox_shared_voice\">%s がボイスメッセージを送信しました</string>\n    <string name=\"dms_inbox_shared_clip\">%s が @%s のクリップを共有しました</string>\n    <string name=\"dms_inbox_shared_igtv\">%s が @%s のIGTVビデオを共有しました</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">次のストーリーに返信しました: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s があなたのストーリーに返信しました: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">次のストーリーに返信しました: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s があなたのストーリーに返信しました: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">ストーリーで @%s をメンションしました</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s がストーリーであなたをメンションしました</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>不明なメディアタイプ</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">メディアの有効期限が切れました！</string>\n    <string name=\"dms_inbox_raven_media_delivered\">送信済み</string>\n    <string name=\"dms_inbox_raven_media_sent\">送信済み</string>\n    <string name=\"dms_inbox_raven_media_opened\">既読</string>\n    <string name=\"dms_inbox_raven_media_replayed\">再生済み</string>\n    <string name=\"dms_inbox_raven_media_sending\">送信中…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">ブロック済</string>\n    <string name=\"dms_inbox_raven_media_suggested\">おすすめ</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">スクリーンショット撮影済み</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">配信できません</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">録音するには長押ししてください</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">チャットから退出する</string>\n    <string name=\"dms_action_leave_question\">チャットを終了しますか?</string>\n    <string name=\"dms_action_kick\">キックする</string>\n    <string name=\"dms_left_users\">退出したユーザー</string>\n    <string name=\"dms_ERROR_INVALID_USER\">無効なユーザー</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">InstagramはDMで60秒以上の動画をアップロードすることを許可していません。</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">InstagramはDMで60秒以上の音声をアップロードすることを許可していません。</string>\n    <string name=\"direct_download_loading\">投稿を取得中</string>\n    <string name=\"downloader_complete\">ダウンロードが完了しました</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">投稿をダウンロード中…</string>\n    <string name=\"downloader_downloading_media\">メディアをダウンロード中</string>\n    <string name=\"downloader_unknown_error\">不明なエラーが発生しました！</string>\n    <string name=\"downloader_error_creating_folder\">フォルダの作成中にエラーが発生しました！</string>\n    <string name=\"downloader_error_download_file\">ダウンロード中にエラーが発生しました</string>\n    <string name=\"comment_viewer_translate_comment\">コメントを翻訳</string>\n    <string name=\"comment_viewer_delete_comment\">コメントを削除</string>\n    <string name=\"followers_type_followers\">フォロワー</string>\n    <string name=\"followers_type_following\">フォロー中</string>\n    <string name=\"followers_compare\">フォロワーとフォロー中を比較しています</string>\n    <string name=\"followers_both_following\">お互いをフォローしています</string>\n    <string name=\"followers_not_following\">%s をフォローしていません</string>\n    <string name=\"followers_not_follower\">%s はあなたをフォローしていません</string>\n    <string name=\"login_error_loading_cookies\">Cookieの読み込みエラー</string>\n    <string name=\"comment_hint\">新しいコメントを書く…</string>\n    <string name=\"liked_notif\">あなたの投稿にいいね！しました</string>\n    <string name=\"comment_notif\">あなたの投稿にコメント:</string>\n    <string name=\"follow_notif\">あなたをフォローしました</string>\n    <string name=\"tagged_notif\">投稿にあなたをタグ付けしました。</string>\n    <string name=\"request_notif\">フォローをリクエストしました</string>\n    <string name=\"request_approve\">リクエストを承認</string>\n    <string name=\"request_reject\">リクエストを拒否</string>\n    <string name=\"share_public_post\">シェア先…</string>\n    <string name=\"share_private_post\">これはプライベートな投稿です！閲覧できる人にシェアしてください。</string>\n    <string name=\"discover_empty\">このカテゴリは空です…</string>\n    <string name=\"update_available\">アップデートが利用可能です！ (%s)</string>\n    <string name=\"updated\">Barinstaをアップデートしていただきありがとうございます！</string>\n    <string name=\"crash_title\">アプリがクラッシュ</string>\n    <string name=\"crash_descr\">アプリがクラッシュしましたが、エラーレポートを開発者に送信して問題の解決を手助けすることができます。(:</string>\n    <string name=\"action_notif\">アクティビティ</string>\n    <string name=\"action_archive\">ストーリーズのアーカイブ</string>\n    <string name=\"action_ayml\">おすすめのユーザー</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d 人のフォロワー</string>\n    <string name=\"activity_count_comments\">%d コメント</string>\n    <string name=\"activity_count_commentlikes\">%d 個のコメントへのいいね！</string>\n    <string name=\"activity_count_usertags\">%d 個のタグ付け</string>\n    <string name=\"activity_count_likes\">%d いいね！</string>\n    <string name=\"activity_count_poy\">%d 枚のあなたの写真</string>\n    <string name=\"activity_count_requests\">%d 件のフォローリクエスト</string>\n    <string name=\"activity_notloggedin\">この通知をクリックする前にログアウトしましたか？</string>\n    <string name=\"feed\">フィード</string>\n    <string name=\"profile\">ユーザー</string>\n    <string name=\"more\">詳細</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d 件選択済み</string>\n    <string name=\"logout_success\">ログアウトに成功しました！</string>\n    <string name=\"dm_thread_info\">情報</string>\n    <string name=\"mark_as_seen\">既読にする</string>\n    <string name=\"version\">バージョン</string>\n    <string name=\"pref_start_screen\">起動画面</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">検索時にキーボードを表示</string>\n    <string name=\"pref_category_general\">全般</string>\n    <string name=\"pref_category_theme\">テーマ</string>\n    <string name=\"pref_category_downloads\">ダウンロード</string>\n    <string name=\"pref_category_locale\">言語</string>\n    <string name=\"account\">アカウント</string>\n    <string name=\"account_hint\">ログインできない場合はアカウントを再度追加してください</string>\n    <string name=\"add_account\">アカウントを追加</string>\n    <string name=\"about_category_license\">ライセンス(英語のみ)</string>\n    <string name=\"about_documentation\">ウェブサイトを開く</string>\n    <string name=\"about_documentation_summary\">サポートを受けて、議論して、他の人に出会って、楽しんでください！</string>\n    <string name=\"about_repository\">GitHubでソースコードを見る</string>\n    <string name=\"about_repository_summary\">監査して、スターを付けて、バグ報告して、貢献して、楽しんでください(二回目)！</string>\n    <string name=\"about_feedback\">メールでフィードバックを送信</string>\n    <string name=\"about_category_3pt\">サードパーティ製アプリ</string>\n    <string name=\"reminder\">注意:</string>\n    <string name=\"reminder_summary\">このアプリは自己責任で使用してください。ダウンロードした画像は、適用される法律で許可されている目的の範囲内でのみ使用することができます。</string>\n    <string name=\"light_white_theme\">ホワイト</string>\n    <string name=\"dark_black_theme\">ブラック</string>\n    <string name=\"light_theme_settings\">ライトテーマ</string>\n    <string name=\"dark_theme_settings\">ダークテーマ</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">マテリアルダーク</string>\n    <string name=\"added_to_favs\">お気に入りに追加しました！</string>\n    <string name=\"add_to_favorites\">お気に入りに追加</string>\n    <string name=\"accounts\">アカウント</string>\n    <string name=\"hashtags\">ハッシュタグ</string>\n    <string name=\"locations\">場所</string>\n    <string name=\"unknown\">不明</string>\n    <string name=\"removed_from_favs\">お気に入りから削除しました!</string>\n    <string name=\"backup_and_restore\">バックアップと復元</string>\n    <string name=\"auto_backup\">自動バックアップ</string>\n    <string name=\"auto_backup_summary\">Android 6 から、Android の自動バックアップ機能は、すべてのアプリ設定、アカウントのログインデータ、お気に入りをGoogleドライブにアップロードします。アプリのアンインストール後に再インストールすると、復元することができます。</string>\n    <string name=\"auto_backup_warning\">この設定は、Google Playサービスが存在しない場合、またはデバイスの設定から自動バックアップが無効になっている場合には影響しません。 ここで無効にしても既存のバックアップは消去されません。</string>\n    <string name=\"auto_backup_setting\">自動バックアップを有効化</string>\n    <string name=\"manual_backup\">手動バックアップ</string>\n    <string name=\"backup_summary\">Barinstaの設定、アカウントのログインデータ、お気に入りをテキストまたは暗号化ファイルにバックアップして、あとで復元できるようにします。</string>\n    <string name=\"backup_warning\">アカウントのログインデータをバックアップする場合は、ファイルを機密として扱い、安全な場所に保管してください!</string>\n    <string name=\"create_backup\">新しいバックアップファイルを作成</string>\n    <string name=\"restore_backup\">既存のバックアップファイルから復元</string>\n    <string name=\"file_chosen_label\">ファイル:</string>\n    <string name=\"enter_password\">パスワードを入力してください</string>\n    <string name=\"select_backup_file\">バックアップ ファイルを選択してください (.zaai/.backup)</string>\n    <string name=\"apply\">適用</string>\n    <string name=\"save\">保存</string>\n    <string name=\"caption\">キャプション</string>\n    <string name=\"edit_caption\">キャプションを編集</string>\n    <string name=\"translate_caption\">翻訳を見る</string>\n    <string name=\"player_timeline_desc\">ビデオプレーヤーのタイムライン</string>\n    <string name=\"liking\">いいね しています…</string>\n    <string name=\"like_unsuccessful\">いいね！に失敗しました</string>\n    <string name=\"unlike_unsuccessful\">いいね！の取消しに失敗しました</string>\n    <string name=\"unliking\">いいね！を取り消しています…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">保存中…</string>\n    <string name=\"removing\">削除中...</string>\n    <string name=\"save_unsuccessful\">保存に失敗しました</string>\n    <string name=\"save_remove_unsuccessful\">削除に失敗しました</string>\n    <string name=\"downloading\">ダウンロード中…</string>\n    <string name=\"downloader_downloading_child\">ダウンロード中 %1$d / %2$d</string>\n    <string name=\"delete\">削除</string>\n    <string name=\"comment\">コメント</string>\n    <string name=\"layout\">レイアウト</string>\n    <string name=\"feed_stories\">フィードのストーリーズ</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">共有</string>\n    <string name=\"layout_style\">レイアウトスタイル</string>\n    <string name=\"column_count\">列数</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">名前を表示</string>\n    <string name=\"show_avatars\">アバターを表示</string>\n    <string name=\"avatar_size\">アバターのサイズ</string>\n    <string name=\"corners\">コーナー</string>\n    <string name=\"show_grid_gap\">グリッドの隙間</string>\n    <string name=\"post_not_found\">投稿が見つかりません！</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">ギャラリー</string>\n    <string name=\"camera\">カメラ</string>\n    <string name=\"all_photos\">すべての写真</string>\n    <string name=\"all_media\">すべてのメディア</string>\n    <string name=\"all_videos\">すべてのビデオ</string>\n    <string name=\"brightness\">明るさ</string>\n    <string name=\"contrast\">コントラスト</string>\n    <string name=\"vibrance\">露出</string>\n    <string name=\"saturation\">彩度</string>\n    <string name=\"sharpen\">シャープ</string>\n    <string name=\"exposure\">露出</string>\n    <string name=\"center\">中央</string>\n    <string name=\"color\">色</string>\n    <string name=\"start\">開始</string>\n    <string name=\"end\">終了</string>\n    <string name=\"bilateral_blur\">バイラテラルフィルター</string>\n    <string name=\"vignette\">ビネット</string>\n    <string name=\"box_blur\">ボックスぼかし</string>\n    <string name=\"sepia\">セピア</string>\n    <string name=\"clarendon\">クラレンドン</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">アデン</string>\n    <string name=\"reset\">リセット</string>\n    <string name=\"crop\">トリミング</string>\n    <string name=\"normal\">ノーマル</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">%d ビュー</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s ストーリー</item>\n    </plurals>\n    <string name=\"details\">詳細</string>\n    <string name=\"title\">タイトル</string>\n    <string name=\"members\">メンバー</string>\n    <string name=\"admin\">管理者</string>\n    <string name=\"inviter\">招待者</string>\n    <string name=\"mute_messages\">メッセージをミュート</string>\n    <string name=\"mute_mentions\">メンションをミュート</string>\n    <string name=\"add_members\">メンバーを追加</string>\n    <string name=\"search\">検索</string>\n    <string name=\"done\">完了</string>\n    <string name=\"dms_action_make_admin\">管理者に設定</string>\n    <string name=\"dms_action_remove_admin\">管理者権限を削除</string>\n    <string name=\"edit_unsuccessful\">編集に失敗しました</string>\n    <string name=\"message\">メッセージ</string>\n    <string name=\"tap_to_remove\">タップして削除</string>\n    <string name=\"forward\">転送</string>\n    <string name=\"forward_outgoing\">メッセージを転送しました</string>\n    <string name=\"forward_incoming\">メッセージを転送しました</string>\n    <string name=\"add\">追加</string>\n    <string name=\"send\">送信</string>\n    <string name=\"replying_to_yourself\">自分に返信しています</string>\n    <string name=\"replying_to_user\">%s に返信しています</string>\n    <string name=\"replied_to_yourself\">自分自身に返信しました</string>\n    <string name=\"replied_you\">返信しました</string>\n    <string name=\"replied_you_group\">%sに返信しました</string>\n    <string name=\"replied_group\">%sに返信しました</string>\n    <string name=\"replied_to_you\">あなたへの返信</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">次のストーリーに返信しました</string>\n    <string name=\"reacted_story_incoming\">あなたのストーリーに反応しました</string>\n    <string name=\"mentioned_story_outgoing\">ストーリーでメンションしました</string>\n    <string name=\"mentioned_story_incoming\">ストーリーであなたをメンションしました</string>\n    <string name=\"replied_story_outgoing\">ストーリーに返信しました</string>\n    <string name=\"replied_story_incoming\">あなたのストーリーに返信しました</string>\n    <string name=\"raven_image_expired\">画像が期限切れです</string>\n    <string name=\"raven_image_info\">画像が表示されると期限切れになります</string>\n    <string name=\"raven_video_expired\">ビデオが期限切れです</string>\n    <string name=\"raven_video_info\">ビデオが表示されると期限切れになります</string>\n    <string name=\"raven_msg_expired\">メッセージが期限切れです</string>\n    <string name=\"raven_msg_info\">メッセージが表示されると期限切れになります</string>\n    <string name=\"story_share\">\\@%sのストーリー</string>\n    <string name=\"story_share_highlight\">\\@%sのストーリーハイライト</string>\n    <string name=\"photo\">写真</string>\n    <string name=\"video\">ビデオ</string>\n    <string name=\"voice_message\">ボイスメッセージ</string>\n    <string name=\"post\">ポスト</string>\n    <string name=\"approval_required_for_new_members\">参加には承認が必要です</string>\n    <string name=\"requests\">リクエスト</string>\n    <string name=\"admins_only\">管理者のみ</string>\n    <string name=\"added_by\">%sに追加されました</string>\n    <string name=\"admin_approval_required\">管理者の承認が必要です</string>\n    <string name=\"admin_approval_required_description\">グループに新しいメンバーを追加するには管理者の承認が必要です</string>\n    <string name=\"dms_action_end\">チャットを終了</string>\n    <string name=\"dms_action_end_question\">チャットを終了しますか？</string>\n    <string name=\"dms_action_end_description\">すべてのメンバーがグループから削除されます。メンバーはチャット履歴を表示することができます。</string>\n    <string name=\"pending_requests\">保留中のリクエスト</string>\n    <string name=\"accept_request_from_user\">%1s (%2s) からのリクエストを承認しますか？</string>\n    <string name=\"decline\">拒否する</string>\n    <string name=\"accept\">承認する</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">保留中のリクエストはありません</string>\n    <string name=\"checking_for_new_messages\">新着メッセージを確認中</string>\n    <string name=\"pref_category_stories\">ストーリーズ</string>\n    <string name=\"pref_category_dm\">メッセージ</string>\n    <string name=\"pref_category_notifications\">通知</string>\n    <string name=\"pref_category_post\">ポスト</string>\n    <string name=\"enable_dm_notifications\">DMの通知を有効にする</string>\n    <string name=\"enable_dm_auto_refesh\">メッセージを自動更新する</string>\n    <string name=\"auto_refresh_every\">自動更新する間隔</string>\n    <string name=\"secs\">秒</string>\n    <string name=\"mins\">分</string>\n    <string name=\"search_giphy\">GIPHYを検索</string>\n    <string name=\"generic_null_response\">返信がありません！</string>\n    <string name=\"generic_not_ok_response\">返信のステータスが正しくありません！</string>\n    <string name=\"generic_failed_request\">リクエストに失敗しました！</string>\n    <string name=\"hint_keyword\">キーワード</string>\n    <string name=\"toggle_keyword_filter\">キーワードフィルタを有効にする</string>\n    <string name=\"edit_keyword_filter\">キーワードフィルタを編集する</string>\n    <string name=\"added_keywords\">リストをフィルタするキーワード: %s を追加しました</string>\n    <string name=\"removed_keywords\">リストをフィルタするキーワード: %s を削除しました</string>\n    <string name=\"marked_as_seen\">既読にする</string>\n    <string name=\"delete_unsuccessful\">削除に失敗しました</string>\n    <string name=\"throttle_error\">APIリクエストが多すぎるためInstagramから制限を受けました。しばらく待ってから再試行してください。</string>\n    <string name=\"error\">エラー</string>\n    <string name=\"account_logged_out\">このアカウントはログアウトされました。</string>\n    <string name=\"login_required\">ログインが必要です！</string>\n    <string name=\"inactive_user\">ユーザーは無効です！</string>\n    <string name=\"crash_report_subject\">Barinstaクラッシュレポート</string>\n    <string name=\"crash_report_title\">クラッシュログを送信するメールアプリを選択してください</string>\n    <string name=\"not_found\">見つかりません！</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">このアップデートをスキップ</string>\n    <string name=\"on_latest_version\">すでに最新バージョンをご利用中です</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">タブの順序は次回の起動時に反映されます</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">キャプションをコピー</string>\n    <string name=\"copy_reply\">返信をコピー</string>\n    <string name=\"restore\">復元</string>\n    <string name=\"backup\">バックアップ</string>\n    <string name=\"dir_select_default_message\">Barinstaがダウンロードと一時ファイルを保存できるフォルダを選択してください。\\n\\n後ほど &gt; 設定 &gt; ダウンロードから変更できます。</string>\n    <string name=\"dir_select_reselect_message\">Androidは、アプリがストレージ上のファイルやディレクトリにアクセスする方法を変更しました。Barinstaは次のフォルダにアクセスする権限を持っていません。</string>\n    <string name=\"dir_select_permission_revoked_message\">以前に選択したフォルダの権限はシステムによって取り消されました:</string>\n    <string name=\"dir_select_folder_not_exist\">以前に選択したフォルダは現在存在しません:</string>\n    <string name=\"dir_select_message2\">ディレクトリを再度選択するか、下のボタンをクリックして新しいディレクトリを選択してください。</string>\n    <string name=\"select_a_folder\">フォルダーが選択されていません！</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">成功！お待ちください。アプリを起動しています…</string>\n    <string name=\"barinsta_folder\">Barinstaフォルダ</string>\n    <string name=\"top\">関連</string>\n    <string name=\"recent\">最近</string>\n    <string name=\"clear\">クリア</string>\n    <string name=\"no_external_map_app\">有効な地図アプリが見つかりません</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">プロフィール写真が見つかりません！</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-kn/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>System Default</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Auto / Follow System</item>\n        <item>Auto / Follow Battery</item>\n        <item>Dark</item>\n        <item>Light</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram default (Unread then read)</item>\n        <item>From newest to oldest</item>\n        <item>From oldest to newest</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>None</item>\n        <item>\\@</item>\n        <item>at</item>\n        <item>on</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>secs</item>\n        <item>mins</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ko/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>시스템 기본 설정</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>오토 / 시스템 따르기</item>\n        <item>오토 / 배터리 따르기</item>\n        <item>다크 모드</item>\n        <item>라이트 모드</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>인스타그램 기본 설정</item>\n        <item>최신순</item>\n        <item>오래된순</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>없음</item>\n        <item>\\@</item>\n        <item>에</item>\n        <item>에</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>초</item>\n        <item>분</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">정보</string>\n    <string name=\"action_dms\">다이렉트</string>\n    <string name=\"action_settings\">설정</string>\n    <string name=\"action_download\">다운로드</string>\n    <string name=\"action_search\">사용자 이름 검색</string>\n    <string name=\"action_compare\">비교</string>\n    <string name=\"clipboard_error\">텍스트 복사 오류</string>\n    <string name=\"clipboard_copied\">클립보드에 복사됨</string>\n    <string name=\"report\">신고</string>\n    <string name=\"set_password\">비밀번호로 파일 보호</string>\n    <string name=\"password_no_max\">비밀번호</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">네</string>\n    <string name=\"cancel\">취소</string>\n    <string name=\"no\">아니오</string>\n    <string name=\"confirm\">확인</string>\n    <string name=\"title_favorites\">즐겨찾기</string>\n    <string name=\"title_discover\">발견</string>\n    <string name=\"title_comments\">댓글</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">활동</string>\n    <string name=\"update_check\">시작시 업데이트 확인</string>\n    <string name=\"flag_secure\">Block screenshots &amp; app preview</string>\n    <string name=\"download_user_folder\">Download posts to username folders</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Mark stories as seen after viewing</string>\n    <string name=\"mark_as_seen_setting_summary\">Story author will know you viewed it</string>\n    <string name=\"hide_muted_reels_setting\">Hide muted stories from feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Mark DM as seen after viewing</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Other members will know you viewed it</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Enable activity notifications</string>\n    <string name=\"story_sort_setting\">Feed stories sort</string>\n    <string name=\"error_loading_profile\">Error loading profile! Is the username valid? If so, you may be ratelimited.</string>\n    <string name=\"error_loading_hashtag\">Error loading hashtag! Is the name valid?</string>\n    <string name=\"error_loading_location\">Error loading location! Is the URL valid?</string>\n    <string name=\"error_creating_folders\">Error creating Download folder(s).</string>\n    <string name=\"select_folder\">폴더 선택</string>\n    <string name=\"theme_settings\">테마</string>\n    <string name=\"select_language\">언어</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\n게시물</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s 게시물</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\n팔로워</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\n팔로잉</string>\n    <string name=\"post_viewer_autoplay_video\">비디오 자동 재생</string>\n    <string name=\"post_viewer_background_play\">백그라운드에서 비디오 계속</string>\n    <string name=\"post_viewer_background_play_summary\">앱의 초점이 맞지 않을 때 비디오를 일시 중지하지 않음</string>\n    <string name=\"post_viewer_muted_autoplay\">비디오 항상 음소거</string>\n    <string name=\"post_viewer_show_captions\">게시물 문구 항상 보임</string>\n    <string name=\"post_viewer_download_dialog_title\">다운로드할 것을 선택하십시오</string>\n    <string name=\"post_viewer_download_current\">현재</string>\n    <string name=\"post_viewer_download_album\">앨범 전체</string>\n    <string name=\"show_stories\">스토리 보임</string>\n    <string name=\"no_more_stories\">새 스토리 없음</string>\n    <string name=\"view_post\">게시물 보기</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">답변 성공!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d 개의 반응 평균 %s</item>\n    </plurals>\n    <string name=\"slider_answer\">내 답변: %s</string>\n    <string name=\"reply_story\">스토리 답장 보내기</string>\n    <string name=\"reply_hint\">답글 달기...</string>\n    <string name=\"story_quiz\">퀴즈</string>\n    <string name=\"story_slider\">슬라이더</string>\n    <string name=\"story_quizzed\">이미 답변하셨습나다.</string>\n    <string name=\"story_mentions\">언급</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">비공개 계정입니다</string>\n    <string name=\"priv_acc_confirm\">언팔로우하면 게시물을 볼 수 없겠습니다. 확실합니까?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">You can log in via More -&gt; Account on the bottom-right corner or you can view public accounts without login!</string>\n    <string name=\"empty_acc\">계정에 게시물이 없음</string>\n    <string name=\"empty_list\">게시물 없음</string>\n    <string name=\"login\">로그인</string>\n    <string name=\"logout\">로그아웃</string>\n    <string name=\"logout_summary\">인스타그램에서 익명으로 검색하기</string>\n    <string name=\"remove_all_acc\">모든 계정 제거</string>\n    <string name=\"remove_all_acc_warning\">This will remove all added accounts from the app!\\nTo remove just one account, long tap the account from the account switcher dialog.\\nDo you want to continue?</string>\n    <string name=\"time_settings\">날짜 형식</string>\n    <string name=\"saved_create_collection\">새 컬렉션 만들기</string>\n    <string name=\"edit_collection\">컬렉션 이름 수정</string>\n    <string name=\"delete_collection\">컬렉션 삭제</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">컬렉션에서 제거</string>\n    <string name=\"liked\">좋아하는 게시물</string>\n    <string name=\"saved\">저장됨</string>\n    <string name=\"tagged\">태그됨</string>\n    <string name=\"dm_person\">메시지</string>\n    <string name=\"follow\">팔로우</string>\n    <string name=\"unfollow\">팔로우 취소</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">즐겨찾기</string>\n    <string name=\"block\">차단</string>\n    <string name=\"unblock\">차단 해제</string>\n    <string name=\"restrict\">제한</string>\n    <string name=\"unrestrict\">제한 해제</string>\n    <string name=\"mute_stories\">스토리 음소거</string>\n    <string name=\"mute_posts\">게시물 음소거</string>\n    <string name=\"unmute_stories\">스토리 음소거 해제</string>\n    <string name=\"unmute_posts\">게시물 음소거 해제</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">소개 복사</string>\n    <string name=\"bio_translate\">소개 번역</string>\n    <string name=\"status_mutual\">서로친구</string>\n    <string name=\"status_following\">팔로잉</string>\n    <string name=\"status_follower\">팔로워</string>\n    <string name=\"map\">맵</string>\n    <string name=\"dialog_export_accounts\">계정</string>\n    <string name=\"dialog_export_settings\">설정</string>\n    <string name=\"dialog_export_favorites\">즐겨찾기</string>\n    <string name=\"dialog_import_success\">Successfully imported!</string>\n    <string name=\"dialog_import_failed\">Failed to import!</string>\n    <string name=\"dialog_export_success\">Successfully exported!</string>\n    <string name=\"dialog_export_failed\">Failed to export!</string>\n    <string name=\"refresh\">새로고침</string>\n    <string name=\"get_cookies\">Get cookies</string>\n    <string name=\"time_settings_title_custom\">Use custom format</string>\n    <string name=\"time_settings_title_separator\">Separator</string>\n    <string name=\"time_settings_title_time_format\">시간 형식</string>\n    <string name=\"time_settings_title_date_format\">날짜 형식</string>\n    <string name=\"time_settings_title_preview\">미리 보기</string>\n    <string name=\"time_settings_swap_time\">Swap Time and Date positions</string>\n    <string name=\"quick_access_cannot_delete_curr\">Cannot delete currently in use account</string>\n    <string name=\"quick_access_confirm_delete\">\\\"%s\\\"을(를) 삭제하시겠습니까?</string>\n    <string name=\"open_profile\">프로필 보기</string>\n    <string name=\"view_story\">스토리 보기</string>\n    <string name=\"view_pfp\">프로필 사진 보기</string>\n    <string name=\"dms_inbox_raven_message_unknown\">지원되지 않는 메시지 유형</string>\n    <string name=\"dms_inbox_unsend\">보내기 취소</string>\n    <string name=\"dms_inbox_giphy\">GIPHY에서 보기</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unknown media type</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media expired!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">전달됨</string>\n    <string name=\"dms_inbox_raven_media_sent\">전송됨</string>\n    <string name=\"dms_inbox_raven_media_opened\">확인함</string>\n    <string name=\"dms_inbox_raven_media_replayed\">재생</string>\n    <string name=\"dms_inbox_raven_media_sending\">전송중…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">차단됨</string>\n    <string name=\"dms_inbox_raven_media_suggested\">추천</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">스크린샷</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Cannot deliver</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Press and hold to record audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">채팅 나가기</string>\n    <string name=\"dms_action_leave_question\">이 채팅에서 나가시겠습니까?</string>\n    <string name=\"dms_action_kick\">Kick</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Fetching post(s)</string>\n    <string name=\"downloader_complete\">Download completed</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Downloading post…</string>\n    <string name=\"downloader_downloading_media\">Downloading media</string>\n    <string name=\"downloader_unknown_error\">Unknown error occurred!!!</string>\n    <string name=\"downloader_error_creating_folder\">Error creating folder!</string>\n    <string name=\"downloader_error_download_file\">Error downloading file</string>\n    <string name=\"comment_viewer_translate_comment\">댓글 번역</string>\n    <string name=\"comment_viewer_delete_comment\">댓글 삭제</string>\n    <string name=\"followers_type_followers\">팔로워</string>\n    <string name=\"followers_type_following\">팔로잉</string>\n    <string name=\"followers_compare\">Comparing followers &amp; following</string>\n    <string name=\"followers_both_following\">Both following each other</string>\n    <string name=\"followers_not_following\">not following %s</string>\n    <string name=\"followers_not_follower\">%s is not following</string>\n    <string name=\"login_error_loading_cookies\">Error loading cookies</string>\n    <string name=\"comment_hint\">Write a new comment…</string>\n    <string name=\"liked_notif\">Liked your post</string>\n    <string name=\"comment_notif\">Commented on your post:</string>\n    <string name=\"follow_notif\">Started following you</string>\n    <string name=\"tagged_notif\">Tagged you in a post</string>\n    <string name=\"request_notif\">Requested following you</string>\n    <string name=\"request_approve\">Approve request</string>\n    <string name=\"request_reject\">Reject request</string>\n    <string name=\"share_public_post\">Share this public post to…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">This category is somehow empty…</string>\n    <string name=\"update_available\">An update is available! (%s)</string>\n    <string name=\"updated\">Thank you for updating Barinsta!</string>\n    <string name=\"crash_title\">App crashed</string>\n    <string name=\"crash_descr\">Oops.. the app crashed, but don\\'t worry you can send error report to the developer to help him fix the issue. (:</string>\n    <string name=\"action_notif\">활동</string>\n    <string name=\"action_archive\">보관됨 스토리</string>\n    <string name=\"action_ayml\">프로필 추천</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d follows</string>\n    <string name=\"activity_count_comments\">%d comments</string>\n    <string name=\"activity_count_commentlikes\">%d comment likes</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d likes</string>\n    <string name=\"activity_count_poy\">%d photos of you</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">피드</string>\n    <string name=\"profile\">프로필</string>\n    <string name=\"more\">더 보기</string>\n    <string name=\"title_dm\">메시지</string>\n    <string name=\"number_selected\">%d selected</string>\n    <string name=\"logout_success\">Successfully logged out!</string>\n    <string name=\"dm_thread_info\">정보</string>\n    <string name=\"mark_as_seen\">Mark as seen</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">시작 화면</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">일반</string>\n    <string name=\"pref_category_theme\">테마</string>\n    <string name=\"pref_category_downloads\">다운로드</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">계정</string>\n    <string name=\"account_hint\">Current login not working? Simply add the account again.</string>\n    <string name=\"add_account\">계정 추가하기</string>\n    <string name=\"about_category_license\">License (English only)</string>\n    <string name=\"about_documentation\">Visit our website</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">See our source code on GitHub</string>\n    <string name=\"about_repository_summary\">Audit, star, report bugs, contribute, and have fun (again)!</string>\n    <string name=\"about_feedback\">Send feedback by email</string>\n    <string name=\"about_category_3pt\">Third-Party Attributions</string>\n    <string name=\"reminder\">알람</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">흰색</string>\n    <string name=\"dark_black_theme\">검은색</string>\n    <string name=\"light_theme_settings\">라이트테마</string>\n    <string name=\"dark_theme_settings\">다크테마</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">머티리얼 다크</string>\n    <string name=\"added_to_favs\">즐겨찾기에 추가되었습니다!</string>\n    <string name=\"add_to_favorites\">즐겨찾기에 추가</string>\n    <string name=\"accounts\">계정</string>\n    <string name=\"hashtags\">해시태그</string>\n    <string name=\"locations\">위치</string>\n    <string name=\"unknown\">알 수 없음</string>\n    <string name=\"removed_from_favs\">즐겨찾기에서 삭제되었습니다!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Restore</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">파일:</string>\n    <string name=\"enter_password\">비밀번호를 입력하세요</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">적용</string>\n    <string name=\"save\">저장</string>\n    <string name=\"caption\">문구</string>\n    <string name=\"edit_caption\">문구 수정</string>\n    <string name=\"translate_caption\">문구 번역</string>\n    <string name=\"player_timeline_desc\">동영상 플레이어 타임라인</string>\n    <string name=\"liking\">좋아요 중...</string>\n    <string name=\"like_unsuccessful\">좋아요 실패</string>\n    <string name=\"unlike_unsuccessful\">좋아요 취소 실패</string>\n    <string name=\"unliking\">좋아요 취소 중...</string>\n    <string name=\"controls\">제어</string>\n    <string name=\"saving\">저장 중...</string>\n    <string name=\"removing\">제거중...</string>\n    <string name=\"save_unsuccessful\">저장 실패</string>\n    <string name=\"save_remove_unsuccessful\">제거 실패</string>\n    <string name=\"downloading\">다운로드 중...</string>\n    <string name=\"downloader_downloading_child\">%2$d중에 %1$d 다운로드</string>\n    <string name=\"delete\">삭제</string>\n    <string name=\"comment\">댓글 달기</string>\n    <string name=\"layout\">레이아웃</string>\n    <string name=\"feed_stories\">피드 스토리</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">공유</string>\n    <string name=\"layout_style\">레이아웃 스타일</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">이름 표시</string>\n    <string name=\"show_avatars\">아바타 표시</string>\n    <string name=\"avatar_size\">아바타 크기</string>\n    <string name=\"corners\">모서리</string>\n    <string name=\"show_grid_gap\">그리드 간격 표시</string>\n    <string name=\"post_not_found\">게시글을 찾지 못했습니다.</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">갤러리</string>\n    <string name=\"camera\">카메라</string>\n    <string name=\"all_photos\">모든 사진</string>\n    <string name=\"all_media\">모든 미디어</string>\n    <string name=\"all_videos\">모든 동영상</string>\n    <string name=\"brightness\">밝기</string>\n    <string name=\"contrast\">대비</string>\n    <string name=\"vibrance\">생동감</string>\n    <string name=\"saturation\">채도</string>\n    <string name=\"sharpen\">선명하게</string>\n    <string name=\"exposure\">노출</string>\n    <string name=\"center\">중앙</string>\n    <string name=\"color\">색</string>\n    <string name=\"start\">시작</string>\n    <string name=\"end\">끝</string>\n    <string name=\"bilateral_blur\">양측 블러</string>\n    <string name=\"vignette\">배경 흐리게</string>\n    <string name=\"box_blur\">박스 흐림</string>\n    <string name=\"sepia\">세피아</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">리셋</string>\n    <string name=\"crop\">잘라내기</string>\n    <string name=\"normal\">보통</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">조회수 %d</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s개 스토리</item>\n    </plurals>\n    <string name=\"details\">상세내용</string>\n    <string name=\"title\">제목</string>\n    <string name=\"members\">멤버</string>\n    <string name=\"admin\">관리자</string>\n    <string name=\"inviter\">초청자</string>\n    <string name=\"mute_messages\">메시지 알림 해제</string>\n    <string name=\"mute_mentions\">언급 알림 해제</string>\n    <string name=\"add_members\">멤버 추가</string>\n    <string name=\"search\">검색</string>\n    <string name=\"done\">완료</string>\n    <string name=\"dms_action_make_admin\">관리자로 지정</string>\n    <string name=\"dms_action_remove_admin\">관리자 제거</string>\n    <string name=\"edit_unsuccessful\">Edit was unsuccessful</string>\n    <string name=\"message\">메시지</string>\n    <string name=\"tap_to_remove\">제거하려면 탭하세요</string>\n    <string name=\"forward\">전달</string>\n    <string name=\"forward_outgoing\">회원님이 메시지를 전달했습니다.</string>\n    <string name=\"forward_incoming\">메시지를 전달했습니다.</string>\n    <string name=\"add\">추가</string>\n    <string name=\"send\">보내기</string>\n    <string name=\"replying_to_yourself\">자신에게 답장 중</string>\n    <string name=\"replying_to_user\">%s에게 답장 중</string>\n    <string name=\"replied_to_yourself\">회원님이 자신에게 답장했습니다</string>\n    <string name=\"replied_you\">회원님이 답장했습니다</string>\n    <string name=\"replied_you_group\">회원님이 %s에게 답장했습니다</string>\n    <string name=\"replied_group\">%s에게 답장했습니다</string>\n    <string name=\"replied_to_you\">회원님에게 답장했습니다</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">회원님이 스토리에 공감했습니다</string>\n    <string name=\"reacted_story_incoming\">회원님의 스토리에 공감했습니다</string>\n    <string name=\"mentioned_story_outgoing\">You mentioned them in your story</string>\n    <string name=\"mentioned_story_incoming\">Mentioned you in their story</string>\n    <string name=\"replied_story_outgoing\">회원님이 스토리에 답장을 보냈습니다</string>\n    <string name=\"replied_story_incoming\">회원님의 스토리에 답장을 보냈습니다</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">사진</string>\n    <string name=\"video\">동영상</string>\n    <string name=\"voice_message\">음성 메시지</string>\n    <string name=\"post\">게시물</string>\n    <string name=\"approval_required_for_new_members\">가입하려면 승인 필요</string>\n    <string name=\"requests\">요청</string>\n    <string name=\"admins_only\">관리자 전용</string>\n    <string name=\"added_by\">%s님이 추가했습니다</string>\n    <string name=\"admin_approval_required\">관리자 승인 필요</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">거절</string>\n    <string name=\"accept\">확인</string>\n    <string name=\"you\">당신</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">스토리</string>\n    <string name=\"pref_category_dm\">메시지</string>\n    <string name=\"pref_category_notifications\">알림</string>\n    <string name=\"pref_category_post\">게시물</string>\n    <string name=\"enable_dm_notifications\">통화 일림 활성화</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">초</string>\n    <string name=\"mins\">분</string>\n    <string name=\"search_giphy\">GIPHY 검색</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">오류</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">문구 복사</string>\n    <string name=\"copy_reply\">답장 복사</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">상단</string>\n    <string name=\"recent\">최근</string>\n    <string name=\"clear\">치우기</string>\n    <string name=\"no_external_map_app\">지도 앱이 없습니다!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-land/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"profile_info_container_bottom_space\">4dp</dimen>\n\n\n    <dimen name=\"profile_picture_size\">70dp</dimen>\n    <dimen name=\"slider_item_size\">65dp</dimen>\n    <dimen name=\"highlight_size\">48dp</dimen>\n\n    <dimen name=\"story_item_height\">70dp</dimen>\n    <dimen name=\"story_item_width\">38dp</dimen>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-mk/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Системски одбрано</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Автоматска тема</item>\n        <item>Автоматска Тема која зависи од нивото на Батеријата</item>\n        <item>Темно</item>\n        <item>Светло</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Стандардно (Од непрочитано па прочитано)</item>\n        <item>Од најнови до најстари</item>\n        <item>Од најстари до најнови</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Ништо</item>\n        <item>\\@</item>\n        <item>на</item>\n        <item>на</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>секунди</item>\n        <item>минути</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-mk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">За</string>\n    <string name=\"action_dms\">Директни Пораки</string>\n    <string name=\"action_settings\">Опции</string>\n    <string name=\"action_download\">Превземи</string>\n    <string name=\"action_search\">Пребарај корисник…</string>\n    <string name=\"action_compare\">Спореди</string>\n    <string name=\"clipboard_error\">Грешка при копирање текст</string>\n    <string name=\"clipboard_copied\">Копрано на clipboard!</string>\n    <string name=\"report\">Пријави</string>\n    <string name=\"set_password\">Заштити податоци со лозинка</string>\n    <string name=\"password_no_max\">Лозинка</string>\n    <string name=\"ok\">ОК</string>\n    <string name=\"yes\">Да</string>\n    <string name=\"cancel\">Откажи</string>\n    <string name=\"no\">Не</string>\n    <string name=\"confirm\">Потврди</string>\n    <string name=\"title_favorites\">Омилени</string>\n    <string name=\"title_discover\">Откриј</string>\n    <string name=\"title_comments\">Коментари</string>\n    <string name=\"title_replies\">Одговори</string>\n    <string name=\"title_notifications\">Активности</string>\n    <string name=\"update_check\">Провери за ажурирање</string>\n    <string name=\"flag_secure\">Блокирај слики од екранот &amp; преглед на апликацијата</string>\n    <string name=\"download_user_folder\">Превземи објави во папката со кориснички имиња</string>\n    <string name=\"download_prepend_username\">Додади корисничко име при зачувување</string>\n    <string name=\"mark_as_seen_setting\">Означи ги приказните како видени</string>\n    <string name=\"mark_as_seen_setting_summary\">Авторот на приказната ќе знае дека сте ја погледнале приказната</string>\n    <string name=\"hide_muted_reels_setting\">Сокриј замолчени приказни</string>\n    <string name=\"dm_mark_as_seen_setting\">Означи порака како видена</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Другите членови ќе знаат дека сте ја виделе содржината на пораката</string>\n    <string name=\"autoplay_stories_setting\">Автоматски гледај видео приказни</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Овозможи нотификации</string>\n    <string name=\"story_sort_setting\">Сортирање на објави</string>\n    <string name=\"error_loading_profile\">Грешка при вчитување на профилот! Проверете дали корисничкото име е валидно?</string>\n    <string name=\"error_loading_hashtag\">Грешка при вчитувањето на хаштагот! Дали е валиден хаштагот?</string>\n    <string name=\"error_loading_location\">Грешка при вчитување на локација! Дали URL адресата е валидна?</string>\n    <string name=\"error_creating_folders\">Фатална Грешка при креирање на папка(и).</string>\n    <string name=\"select_folder\">Одбери папка</string>\n    <string name=\"theme_settings\">Тема</string>\n    <string name=\"select_language\">Јазик</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nОбјава</item>\n        <item quantity=\"other\">%s\\nОбјави</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Објава</item>\n        <item quantity=\"other\">%s Објави</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nСледбеник</item>\n        <item quantity=\"other\">%s\\nСледбеници</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nСледбеници</string>\n    <string name=\"post_viewer_autoplay_video\">Autoplay на видеа</string>\n    <string name=\"post_viewer_background_play\">Овозможи играње на видео во позадина</string>\n    <string name=\"post_viewer_background_play_summary\">Не паузирај видеа кога апликацијата е надвор од фокус</string>\n    <string name=\"post_viewer_muted_autoplay\">Секогаш гледај видеа без звук</string>\n    <string name=\"post_viewer_show_captions\">Секогаш прикажувај наслов</string>\n    <string name=\"post_viewer_download_dialog_title\">Одбери што сакаш да превземеш</string>\n    <string name=\"post_viewer_download_current\">Тековен</string>\n    <string name=\"post_viewer_download_album\">Цел Албум</string>\n    <string name=\"show_stories\">Прикажи приказни</string>\n    <string name=\"no_more_stories\">Нема повеќе приказни!</string>\n    <string name=\"view_post\">Погледни Објава</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Одговарњето беше успешно!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d одговор со просек  %s</item>\n        <item quantity=\"other\">%d одговори со просек %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Твојот одговор: %s</string>\n    <string name=\"reply_story\">Одговори на приказната</string>\n    <string name=\"reply_hint\">Одговори…</string>\n    <string name=\"story_quiz\">Квиз</string>\n    <string name=\"story_slider\">Лизгач</string>\n    <string name=\"story_quizzed\">Вие веќе одговоривте!</string>\n    <string name=\"story_mentions\">Спомнувања</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Корисникот има приватен профил</string>\n    <string name=\"priv_acc_confirm\">Наме да можете да гледате објави и приказни од овој корисник ако го Одследите! Дали сте сигурни?</string>\n    <string name=\"are_you_sure\">Дали сте сигурни?</string>\n    <string name=\"no_acc\">Можете да се најавите преку Повеќе -&gt; Корисничата сметка која се наоѓа долу десно или само можете да гледате Отворени профили без да се логирате!</string>\n    <string name=\"empty_acc\">Овој корисник нема објави</string>\n    <string name=\"empty_list\">Не постојат такви објави!</string>\n    <string name=\"login\">Најави се</string>\n    <string name=\"logout\">Одјави се</string>\n    <string name=\"logout_summary\">Пребарувај инстаграм анонимно</string>\n    <string name=\"remove_all_acc\">Отстрани ги сите кориснички сметки</string>\n    <string name=\"remove_all_acc_warning\">Ова ќе ги отстране сите кориснички сметки од апликацијата!\\n За да отстраните само една корисничка сметка држете со прстот на сметката\\n Дали сакате да продолжите?</string>\n    <string name=\"time_settings\">Формат на датум</string>\n    <string name=\"saved_create_collection\">Креирај нова колекција</string>\n    <string name=\"edit_collection\">Уреди го името на колекцијата</string>\n    <string name=\"delete_collection\">Избриши колекција</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Одстрани од колекција</string>\n    <string name=\"liked\">Лајкнато</string>\n    <string name=\"saved\">Зачувано</string>\n    <string name=\"tagged\">Означен</string>\n    <string name=\"dm_person\">Порака</string>\n    <string name=\"follow\">Следи</string>\n    <string name=\"unfollow\">Одследи</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Омилен</string>\n    <string name=\"block\">Блокирај</string>\n    <string name=\"unblock\">Одблокни</string>\n    <string name=\"restrict\">Ограничи</string>\n    <string name=\"unrestrict\">Одограничи</string>\n    <string name=\"mute_stories\">Игнорирај Приказни</string>\n    <string name=\"mute_posts\">Игнорирај Објави</string>\n    <string name=\"unmute_stories\">Одигнорирај приказни</string>\n    <string name=\"unmute_posts\">Одигнорирај Објави</string>\n    <string name=\"remove_follower\">Отстрани следбеник</string>\n    <string name=\"bio_copy\">Копирај био</string>\n    <string name=\"bio_translate\">Преведи био</string>\n    <string name=\"status_mutual\">Блиски</string>\n    <string name=\"status_following\">Следиш</string>\n    <string name=\"status_follower\">Следач</string>\n    <string name=\"map\">Карта</string>\n    <string name=\"dialog_export_accounts\">Корисничка сметка</string>\n    <string name=\"dialog_export_settings\">Подесувања</string>\n    <string name=\"dialog_export_favorites\">Омилени</string>\n    <string name=\"dialog_import_success\">Успешно импортирање!</string>\n    <string name=\"dialog_import_failed\">Неуспешно Импортирање!</string>\n    <string name=\"dialog_export_success\">Успешно експортиање!</string>\n    <string name=\"dialog_export_failed\">Неуспешно експортиање!</string>\n    <string name=\"refresh\">Обнови</string>\n    <string name=\"get_cookies\">Превземи колачиња</string>\n    <string name=\"time_settings_title_custom\">Користи свој формат</string>\n    <string name=\"time_settings_title_separator\">Разделувач</string>\n    <string name=\"time_settings_title_time_format\">Формат на време</string>\n    <string name=\"time_settings_title_date_format\">Формат на датум</string>\n    <string name=\"time_settings_title_preview\">Преглед</string>\n    <string name=\"time_settings_swap_time\">Замени ги местата на датумот и времето</string>\n    <string name=\"quick_access_cannot_delete_curr\">Неможе да избришите сметка која се користи</string>\n    <string name=\"quick_access_confirm_delete\">Дали сте сигурни дека сакате да избришите \\'%s\\'?</string>\n    <string name=\"open_profile\">Отвори Профил</string>\n    <string name=\"view_story\">Погледни приказна</string>\n    <string name=\"view_pfp\">Прегладај профилната слика</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Овој вид на порака е неподржан</string>\n    <string name=\"dms_inbox_unsend\">Избриши порака</string>\n    <string name=\"dms_inbox_giphy\">Погледни на GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s сподели објава од @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s сподели слика</string>\n    <string name=\"dms_inbox_shared_video\">%s сподели видео</string>\n    <string name=\"dms_inbox_shared_message\">%s прати порака</string>\n    <string name=\"dms_inbox_shared_gif\">%s сподели gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s сподели лепенка</string>\n    <string name=\"dms_inbox_shared_profile\">%s сподели профил: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s сподели локација:%s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s сподели приказна од @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s сподели приказна од @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s прати гласовна порака</string>\n    <string name=\"dms_inbox_shared_clip\">%s сподели клип од @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s сподели IGTV видео од @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Ти изреагира на нивната приказна: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s ти одгорвори на твојата приказна:%s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Ти изреагира на нивната приказна: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s ти одгорвори на твојата приказна:%s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Ги спомна @%s на твојата приказна</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s те спомна на нивната приказна</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Непознат вид на порака</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Истече времето на медиумот!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Пратено</string>\n    <string name=\"dms_inbox_raven_media_sent\">Пратено</string>\n    <string name=\"dms_inbox_raven_media_opened\">Отворено</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Одговорено</string>\n    <string name=\"dms_inbox_raven_media_sending\">Се праќа…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Блокнат</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Предложено</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Скриншотнато</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Неможе да се прати</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Притисни и задржи за да снимиш аудио порака</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Напушти чат</string>\n    <string name=\"dms_action_leave_question\">Дали сакате да го напуштите чатот?</string>\n    <string name=\"dms_action_kick\">Кикни</string>\n    <string name=\"dms_left_users\">Поранешни корисници</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Невалиден корисник</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Инстаграм не дозволува видеа подолги од 60 секунди во пораки.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Инстаграм не дозволува аудио пораки подолги од 60 секунди.</string>\n    <string name=\"direct_download_loading\">Се превземаат објава(ви)</string>\n    <string name=\"downloader_complete\">Преземањето е завршено</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Превземање на Објава…</string>\n    <string name=\"downloader_downloading_media\">Се превзема медиум</string>\n    <string name=\"downloader_unknown_error\">Непозната Фатална Грешка!!!</string>\n    <string name=\"downloader_error_creating_folder\">Фатална грешка при креирање папка!</string>\n    <string name=\"downloader_error_download_file\">Фатална грешка при превзамање фајл</string>\n    <string name=\"comment_viewer_translate_comment\">Преведи коментар</string>\n    <string name=\"comment_viewer_delete_comment\">Избриши коментар</string>\n    <string name=\"followers_type_followers\">Следачи</string>\n    <string name=\"followers_type_following\">Оние кои ги следиш</string>\n    <string name=\"followers_compare\">Споредување следбеници &amp; following</string>\n    <string name=\"followers_both_following\">Обата се следат меѓу себе</string>\n    <string name=\"followers_not_following\">не ве следат %s</string>\n    <string name=\"followers_not_follower\">%s не ве следи</string>\n    <string name=\"login_error_loading_cookies\">Грешка при вчитување колачиња</string>\n    <string name=\"comment_hint\">Напиши нов коментар…</string>\n    <string name=\"liked_notif\">Ја лајкна вашата објава</string>\n    <string name=\"comment_notif\">Коментира на твојата објава:</string>\n    <string name=\"follow_notif\">Започна да те следи</string>\n    <string name=\"tagged_notif\">Те тагна во објава</string>\n    <string name=\"request_notif\">Пуште барање за следење</string>\n    <string name=\"request_approve\">Одобри барање</string>\n    <string name=\"request_reject\">Не одобрувај барање</string>\n    <string name=\"share_public_post\">Сподели ја оваа јавна објава до…</string>\n    <string name=\"share_private_post\">Ова е приватна објава! Споделете ја со оние кои можат да ја видат.</string>\n    <string name=\"discover_empty\">Оваа категорија е празна…</string>\n    <string name=\"update_available\">Нова надоградба е присутна! (%s)</string>\n    <string name=\"updated\">Ви благодариме за надоградбата на Barinsta!</string>\n    <string name=\"crash_title\">Апликацијата крашна</string>\n    <string name=\"crash_descr\">аааагхххх.. апликацијаа крашна, ама не се грижете, можете да пратите листа на фатални грешки кај програмерите за да ви помогнат да се поправи проблемот. (:</string>\n    <string name=\"action_notif\">Активности</string>\n    <string name=\"action_archive\">Архива на приказни</string>\n    <string name=\"action_ayml\">Препорачани кориснчки сметки</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d следачи</string>\n    <string name=\"activity_count_comments\">%d коментари</string>\n    <string name=\"activity_count_commentlikes\">%d лајкови на коментари</string>\n    <string name=\"activity_count_usertags\">%d корисничи тагови</string>\n    <string name=\"activity_count_likes\">%d лајкови</string>\n    <string name=\"activity_count_poy\">%d фотографии од тебе</string>\n    <string name=\"activity_count_requests\">%d барања за следење</string>\n    <string name=\"activity_notloggedin\">Се одјавивте пред да притисните на нотификацијата?!</string>\n    <string name=\"feed\">Објави</string>\n    <string name=\"profile\">Профил</string>\n    <string name=\"more\">Повеќе</string>\n    <string name=\"title_dm\">Пораки</string>\n    <string name=\"number_selected\">%d селктирано</string>\n    <string name=\"logout_success\">Успешно се одјавивте!</string>\n    <string name=\"dm_thread_info\">Информации</string>\n    <string name=\"mark_as_seen\">Означи како видено</string>\n    <string name=\"version\">Верзија</string>\n    <string name=\"pref_start_screen\">Почетна страна</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Прикажи тастатура при пребарување</string>\n    <string name=\"pref_category_general\">Општо</string>\n    <string name=\"pref_category_theme\">Изглед</string>\n    <string name=\"pref_category_downloads\">Превземања</string>\n    <string name=\"pref_category_locale\">Јазик</string>\n    <string name=\"account\">Сметка</string>\n    <string name=\"account_hint\">Сегашното најавување не работи? Пробајте повторно.</string>\n    <string name=\"add_account\">Додадете сметка</string>\n    <string name=\"about_category_license\">Лиценца (само на Англиски)</string>\n    <string name=\"about_documentation\">Посете ја нашата веб-страна</string>\n    <string name=\"about_documentation_summary\">Пронајдете подршка, дискутирајте, запознајте се со други, и забавувајте се!</string>\n    <string name=\"about_repository\">Погледнете го изворниот код на GitHub</string>\n    <string name=\"about_repository_summary\">Ревидирајте, додадете ѕвезда, пријавете грешки и багови, контрибуирајте и имајте забава!</string>\n    <string name=\"about_feedback\">Пратете пофалби/поплаки преку email</string>\n    <string name=\"about_category_3pt\">Интеграција на Third-Party</string>\n    <string name=\"reminder\">Потсетник</string>\n    <string name=\"reminder_summary\">Ве молиме користете ја апликацијата со одговорност. Превземените содржини како слики, видеа и аудио, треба да бидат користени со применливите закони на Инстаграм и авторите на постот/приказната.</string>\n    <string name=\"light_white_theme\">Бела</string>\n    <string name=\"dark_black_theme\">Црна</string>\n    <string name=\"light_theme_settings\">Светла тема</string>\n    <string name=\"dark_theme_settings\">Темна тема</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Материјално Црна</string>\n    <string name=\"added_to_favs\">Додадено во Омилени!</string>\n    <string name=\"add_to_favorites\">Додади во Омилени</string>\n    <string name=\"accounts\">Акаунти</string>\n    <string name=\"hashtags\">Хаштагови</string>\n    <string name=\"locations\">Локации</string>\n    <string name=\"unknown\">Непознато</string>\n    <string name=\"removed_from_favs\">Одстрането од Омилени!</string>\n    <string name=\"backup_and_restore\">Направете Резервна Копија &amp; Враќање</string>\n    <string name=\"auto_backup\">Автоматска резерва</string>\n    <string name=\"auto_backup_summary\">Со Андроид верзија 6, Андроид автоматското зачувување на копија ќе зачувува, опции, кориснички информации, омилиени, во Google Drive, и кои ќе бидат вратени со реинсталација на апликацијата.</string>\n    <string name=\"auto_backup_warning\">Оваа опција нема ефект, ако немате Google Play Services, или ако Автоматското зачувување на копии е исклучено од подесување. Исклучувањето од тука нема да избрише постоечки резервни копии.</string>\n    <string name=\"auto_backup_setting\">Уклучи Автоматски Резерви</string>\n    <string name=\"manual_backup\">Рачни Резервни Копии</string>\n    <string name=\"backup_summary\">Зачувај опции од апликацијата, кориснички профил, и/или информации од твоите омилени профили во обичен текст или со енкрипција за подоцна да можеш да ги вратиш.</string>\n    <string name=\"backup_warning\">Ако зачувуваш информации за најавување, третирај го генералниот фајл како многу важен, чувај го на безбедно место!</string>\n    <string name=\"create_backup\">Креирај новa резервна копија</string>\n    <string name=\"restore_backup\">Поврати информации од резервна копија</string>\n    <string name=\"file_chosen_label\">Фајл:</string>\n    <string name=\"enter_password\">Внесете лозинка</string>\n    <string name=\"select_backup_file\">Одбери backup фајл (.zaai/.backup)</string>\n    <string name=\"apply\">Примени</string>\n    <string name=\"save\">Зачувај</string>\n    <string name=\"caption\">Наслов</string>\n    <string name=\"edit_caption\">Промени наслов</string>\n    <string name=\"translate_caption\">Преведи наслов</string>\n    <string name=\"player_timeline_desc\">Лента на Видеоплеер</string>\n    <string name=\"liking\">Се лајкнува…</string>\n    <string name=\"like_unsuccessful\">Лајкнувањето беше неуспешно</string>\n    <string name=\"unlike_unsuccessful\">Одлајкнувањето беше неуспешно</string>\n    <string name=\"unliking\">Се одлајкнува…</string>\n    <string name=\"controls\">Контроли</string>\n    <string name=\"saving\">Се зачувува…</string>\n    <string name=\"removing\">Се отстранува…</string>\n    <string name=\"save_unsuccessful\">Зачувувањето неуспешно</string>\n    <string name=\"save_remove_unsuccessful\">Отстранувањето беше неуспешно</string>\n    <string name=\"downloading\">Се превзема…</string>\n    <string name=\"downloader_downloading_child\">Превземање податок %1$d од %2$d</string>\n    <string name=\"delete\">Избриши</string>\n    <string name=\"comment\">Коментирај</string>\n    <string name=\"layout\">Изглед</string>\n    <string name=\"feed_stories\">Приказни</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Сподели</string>\n    <string name=\"layout_style\">Стил на изглед</string>\n    <string name=\"column_count\">Број на колони</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Прикажи имиња</string>\n    <string name=\"show_avatars\">Прикажи Аватар</string>\n    <string name=\"avatar_size\">Големина на Аватар</string>\n    <string name=\"corners\">Ќошеви</string>\n    <string name=\"show_grid_gap\">Прикажи растојание меѓу слики</string>\n    <string name=\"post_not_found\">Објавата не беше пронајдена!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Галерија</string>\n    <string name=\"camera\">Камера</string>\n    <string name=\"all_photos\">Сите слики</string>\n    <string name=\"all_media\">Сите Медиуми</string>\n    <string name=\"all_videos\">Сите видеа</string>\n    <string name=\"brightness\">Осветленост</string>\n    <string name=\"contrast\">Контраст</string>\n    <string name=\"vibrance\">Јачина на боја</string>\n    <string name=\"saturation\">Заситеност</string>\n    <string name=\"sharpen\">Заостри</string>\n    <string name=\"exposure\">Изложеност</string>\n    <string name=\"center\">Центар</string>\n    <string name=\"color\">Боја</string>\n    <string name=\"start\">Почеток</string>\n    <string name=\"end\">Крај</string>\n    <string name=\"bilateral_blur\">Билатерална заматеност</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Заматување</string>\n    <string name=\"sepia\">Сепија</string>\n    <string name=\"clarendon\">Кларендон</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Аден</string>\n    <string name=\"reset\">Ресетирај</string>\n    <string name=\"crop\">Скрати</string>\n    <string name=\"normal\">Нормално</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d преглед</item>\n        <item quantity=\"other\">%d прегледи</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s приказна</item>\n        <item quantity=\"other\">%s приказни</item>\n    </plurals>\n    <string name=\"details\">Детали</string>\n    <string name=\"title\">Наслов</string>\n    <string name=\"members\">Членови</string>\n    <string name=\"admin\">Админ</string>\n    <string name=\"inviter\">Поканувач</string>\n    <string name=\"mute_messages\">Игнорирај пораки</string>\n    <string name=\"mute_mentions\">Игнорирај спомнувања</string>\n    <string name=\"add_members\">Додади учесници</string>\n    <string name=\"search\">Пребарај</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"dms_action_make_admin\">Поставете Админ</string>\n    <string name=\"dms_action_remove_admin\">Одстрани од Администратор</string>\n    <string name=\"edit_unsuccessful\">Уредувањето беше неуспешно</string>\n    <string name=\"message\">Порака</string>\n    <string name=\"tap_to_remove\">Допри за да одтсраниш</string>\n    <string name=\"forward\">Препрати</string>\n    <string name=\"forward_outgoing\">Ти препрати порака</string>\n    <string name=\"forward_incoming\">Препрати порака</string>\n    <string name=\"add\">Додади</string>\n    <string name=\"send\">Прати</string>\n    <string name=\"replying_to_yourself\">Одговорара на себе</string>\n    <string name=\"replying_to_user\">Одговара на %s</string>\n    <string name=\"replied_to_yourself\">Си одговори на себе</string>\n    <string name=\"replied_you\">Ти одговори</string>\n    <string name=\"replied_you_group\">Ти одговори на %s</string>\n    <string name=\"replied_group\">Одговори на %s</string>\n    <string name=\"replied_to_you\">Тебе ти одговори</string>\n    <string name=\"replied_to_themself\">Си одговори себеси</string>\n    <string name=\"reacted_story_outgoing\">Ти реагира на приказната</string>\n    <string name=\"reacted_story_incoming\">Изреагира на твојата приказна</string>\n    <string name=\"mentioned_story_outgoing\">Ги спомна нив на твојата приказна</string>\n    <string name=\"mentioned_story_incoming\">Те спомна тебе на нивната приказна</string>\n    <string name=\"replied_story_outgoing\">Ти одговори на нивната приказна</string>\n    <string name=\"replied_story_incoming\">Одговори на твојата приказна</string>\n    <string name=\"raven_image_expired\">Му помина рокот на сликата</string>\n    <string name=\"raven_image_info\">Сликата ќе исчезне кога ќе биде погледната</string>\n    <string name=\"raven_video_expired\">Истече рокот на видеото</string>\n    <string name=\"raven_video_info\">Видеото ќе исчезне кога ќе биде погледнато</string>\n    <string name=\"raven_msg_expired\">Истече рокот на пораката</string>\n    <string name=\"raven_msg_info\">Ќе истече рокот на пораката кога ќе ја погледните</string>\n    <string name=\"story_share\">Приказна на @%s</string>\n    <string name=\"story_share_highlight\">Нагласена приказна на @%s </string>\n    <string name=\"photo\">Слика</string>\n    <string name=\"video\">Видео</string>\n    <string name=\"voice_message\">Гласовна порака</string>\n    <string name=\"post\">Објава</string>\n    <string name=\"approval_required_for_new_members\">Потребно е одобрување за да се придружиш</string>\n    <string name=\"requests\">Барања</string>\n    <string name=\"admins_only\">Само за Админи</string>\n    <string name=\"added_by\">Додадено од %s</string>\n    <string name=\"admin_approval_required\">Одобрување од админ е потребно</string>\n    <string name=\"admin_approval_required_description\">Потребно е одобрување од админ за додавање на нови членови во групата</string>\n    <string name=\"dms_action_end\">Заврши разговор</string>\n    <string name=\"dms_action_end_question\">Заврши разговор?</string>\n    <string name=\"dms_action_end_description\">Сите членови ќе бидат остранети од групата. Тие ќе имаат сеуште пристап до историјата на разговорот.</string>\n    <string name=\"pending_requests\">Барања во очекување</string>\n    <string name=\"accept_request_from_user\">Прифатете го барањето од %1s (%2s)?</string>\n    <string name=\"decline\">Одбиј</string>\n    <string name=\"accept\">Прифати</string>\n    <string name=\"you\">Ти</string>\n    <string name=\"no_pending_requests\">Нема барања во очекување</string>\n    <string name=\"checking_for_new_messages\">Се проверува за нови пораки</string>\n    <string name=\"pref_category_stories\">Приказни</string>\n    <string name=\"pref_category_dm\">Пораки</string>\n    <string name=\"pref_category_notifications\">Нотификации</string>\n    <string name=\"pref_category_post\">Објави</string>\n    <string name=\"enable_dm_notifications\">Овозможи Нотификации за пораки</string>\n    <string name=\"enable_dm_auto_refesh\">Автоматски освежи пораки</string>\n    <string name=\"auto_refresh_every\">Автоматски освежи на секои</string>\n    <string name=\"secs\">секунди</string>\n    <string name=\"mins\">минути</string>\n    <string name=\"search_giphy\">Пребарај на GIPHY</string>\n    <string name=\"generic_null_response\">Одбоворот е null!</string>\n    <string name=\"generic_not_ok_response\">Одговорот на статусот не е ок!</string>\n    <string name=\"generic_failed_request\">Барањето неуспешно!</string>\n    <string name=\"hint_keyword\">Клучен збор</string>\n    <string name=\"toggle_keyword_filter\">Вклучи филтер на зборови</string>\n    <string name=\"edit_keyword_filter\">Уреди филтер на зборови</string>\n    <string name=\"added_keywords\">Додаден клучниот збор: %s во листата на филтер</string>\n    <string name=\"removed_keywords\">Отстранет клучниот збор: %s од листата на филтер</string>\n    <string name=\"marked_as_seen\">Означено како видено</string>\n    <string name=\"delete_unsuccessful\">Отстранувањето неуспешно</string>\n    <string name=\"throttle_error\">Успорување поради Instagram, премногу API пребарувања. Ве молиме почекајте некое време, пред да пробате повторно.</string>\n    <string name=\"error\">Грешка</string>\n    <string name=\"account_logged_out\">Оваа сметка е одјавена.</string>\n    <string name=\"login_required\">Потребна е најава!</string>\n    <string name=\"inactive_user\">Корисникот е неактивен!</string>\n    <string name=\"crash_report_subject\"> Пријави Грешка </string>\n    <string name=\"crash_report_title\">Одберете еmail апликација за праќање на грешки</string>\n    <string name=\"not_found\">Не беше пронајдено!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Прескокни го ова ажурирање</string>\n    <string name=\"on_latest_version\">Веќе ја користите најновата верзија</string>\n    <string name=\"tab_order\">Подредување</string>\n    <string name=\"other_tabs\">Други менија</string>\n    <string name=\"tab_order_start_next_launch\">Редоследот на менија ќе биде аплициран на следно лансирање</string>\n    <string name=\"dm_remove_warning\">Ако го зачувате, сите карактеристики поврзани со Директни Пораки ќе бидат исклучени на следно лансирање</string>\n    <string name=\"copy_caption\">Копирај наслов</string>\n    <string name=\"copy_reply\">Копирај одговор</string>\n    <string name=\"restore\">Врати</string>\n    <string name=\"backup\">Резерви</string>\n    <string name=\"dir_select_default_message\">Одберете фолдер каде што Barinsta ќе ги зачувува сите привремени податоци.\\n\\n Можете да го промените ова подоцна во Повеќе &gt; Опции &gt; Превземања.</string>\n    <string name=\"dir_select_reselect_message\">Андроид 12 го промени начинот на кои апликациите можат да пристапуваат кон податоци од меморија. Засега Barinsta не го подржува ова:</string>\n    <string name=\"dir_select_permission_revoked_message\">Дозволите за претходниот одбран фолдер ќе бидат вратени од страна на системот:</string>\n    <string name=\"dir_select_folder_not_exist\">Претходно одбраниот фолдер не постои:</string>\n    <string name=\"dir_select_message2\">Одберете пак директориум или направете нов со притскање на копчето подолу.</string>\n    <string name=\"select_a_folder\">Немате одберено фолдер!</string>\n    <string name=\"dir_select_no_download_folder\">Одберете фолдер од вашата меморија, а Не категорија од страна.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Успешно! Почекајте...</string>\n    <string name=\"barinsta_folder\">Barinsta фолдер</string>\n    <string name=\"top\">Врв</string>\n    <string name=\"recent\">Најнови</string>\n    <string name=\"clear\">Исчисти</string>\n    <string name=\"no_external_map_app\">Нема апликација со Мапи!</string>\n    <string name=\"click_to_show_full\">Прилажи прецизен број на допаѓања</string>\n    <string name=\"no_profile_pic_found\">Слика на профил не беше пронајдена!</string>\n    <string name=\"swipe_up_confirmation\">Дали сте сигурни дека сакате да го отворите овој линк?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Сподели преку ДП</string>\n    <string name=\"share_link\">Сподели линк…</string>\n    <string name=\"slide_to_cancel\">Повлечете за да Откажите</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/bool.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <bool name=\"isNight\">true</bool>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"dm_profile_button_color\">#353535</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!--\n    <style name=\"AppTheme.LaunchTheme\" parent=\"AppTheme\">\n        <item name=\"android:windowBackground\">@drawable/launch_dark</item>\n    </style>\n    -->\n\n    <style name=\"AppTheme.BottomSheetDialog\" parent=\"Theme.MaterialComponents.BottomSheetDialog\" />\n\n    <style name=\"PlayButtonCard\" parent=\"CardView.Light\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nl/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Systeemstandaard</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automatisch / Volgsysteem</item>\n        <item>Automatisch / Volg batterij</item>\n        <item>Donker</item>\n        <item>Licht</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram standaard (Ongelezen dan gelezen)</item>\n        <item>Sorteren van nieuw naar oud</item>\n        <item>Sorteren van oud naar nieuw</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Geen</item>\n        <item>\\@</item>\n        <item>bij</item>\n        <item>op</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>seconden</item>\n        <item>minuten</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Over</string>\n    <string name=\"action_dms\">Privé berichten</string>\n    <string name=\"action_settings\">Instellingen</string>\n    <string name=\"action_download\">Download</string>\n    <string name=\"action_search\">Zoek gebruikersnaam…</string>\n    <string name=\"action_compare\">Vergelijk</string>\n    <string name=\"clipboard_error\">Fout bij kopiëren van tekst</string>\n    <string name=\"clipboard_copied\">Gekopiëerd naar klembord!</string>\n    <string name=\"report\">Melden</string>\n    <string name=\"set_password\">Bescherm bestand met wachtwoord</string>\n    <string name=\"password_no_max\">Wachtwoord</string>\n    <string name=\"ok\">Oké</string>\n    <string name=\"yes\">Ja</string>\n    <string name=\"cancel\">Annuleren</string>\n    <string name=\"no\">Nee</string>\n    <string name=\"confirm\">Bevestigen</string>\n    <string name=\"title_favorites\">Favorieten</string>\n    <string name=\"title_discover\">Ontdekken</string>\n    <string name=\"title_comments\">Opmerkingen</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Activiteit</string>\n    <string name=\"update_check\">Controleer op updates bij het opstarten</string>\n    <string name=\"flag_secure\">Screenshots &amp; app voorbeeld blokkeren</string>\n    <string name=\"download_user_folder\">Download berichten naar gebruikersnaam mappen</string>\n    <string name=\"download_prepend_username\">Gebruikersnaam toevoegen aan bestandsnaam</string>\n    <string name=\"mark_as_seen_setting\">Markeer verhalen als gelezen na bekijken</string>\n    <string name=\"mark_as_seen_setting_summary\">Verhaalmaker zal het weten als je het bekeken hebt</string>\n    <string name=\"hide_muted_reels_setting\">Verberg gedempte verhalen uit feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Markeer privéberichten als gelezen na bekijken</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Andere gebruikers zullen het weten als je het hebt bekeken</string>\n    <string name=\"autoplay_stories_setting\">Videoverhalen automatisch afspelen</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Activiteitmeldingen inschakelen</string>\n    <string name=\"story_sort_setting\">Feedverhalen sorteren</string>\n    <string name=\"error_loading_profile\">Fout bij het laden van profiel! Is de gebruikersnaam geldig? Zo ja, dan kan je geratelimiteerd zijn.</string>\n    <string name=\"error_loading_hashtag\">Fout bij het laden van hashtag! Is de naam geldig?</string>\n    <string name=\"error_loading_location\">Fout bij het laden van locatie! Is de URL geldig?</string>\n    <string name=\"error_creating_folders\">Fout bij maken download map(en).</string>\n    <string name=\"select_folder\">Selecteer map</string>\n    <string name=\"theme_settings\">Thema</string>\n    <string name=\"select_language\">Taal</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPost</item>\n        <item quantity=\"other\">%s\\nPosts</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Post</item>\n        <item quantity=\"other\">%s Berichten</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nVolger</item>\n        <item quantity=\"other\">%s\\nVolgers</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nVolgend</string>\n    <string name=\"post_viewer_autoplay_video\">Video\\'s automatisch afspelen</string>\n    <string name=\"post_viewer_background_play\">Doorgaan met video\\'s op achtergrond</string>\n    <string name=\"post_viewer_background_play_summary\">Video\\'s niet pauzeren als de app niet meer op focus is</string>\n    <string name=\"post_viewer_muted_autoplay\">Video\\'s altijd dempen</string>\n    <string name=\"post_viewer_show_captions\">Toon altijd post bijschriften</string>\n    <string name=\"post_viewer_download_dialog_title\">Selecteer wat je wil downloaden</string>\n    <string name=\"post_viewer_download_current\">Huidige</string>\n    <string name=\"post_viewer_download_album\">Volledige Album</string>\n    <string name=\"show_stories\">Toon verhalen</string>\n    <string name=\"no_more_stories\">Geen verhalen meer!</string>\n    <string name=\"view_post\">Bekijk Bericht</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Antwoord gelukt!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d reactie gemiddeld %s</item>\n        <item quantity=\"other\">%d reacties gemiddeld %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Uw antwoord: %s</string>\n    <string name=\"reply_story\">Reageer op het verhaal</string>\n    <string name=\"reply_hint\">Reageer…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Schuifregelaar</string>\n    <string name=\"story_quizzed\">Je hebt al geantwoord!</string>\n    <string name=\"story_mentions\">Vermeldingen</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Dit Account is Privé</string>\n    <string name=\"priv_acc_confirm\">Je zal geen toegang meer hebben tot berichten na het ontvolgen! Weet je het zeker?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Je kunt inloggen via Meer -&gt; Rechtsonder in je account, of je kunt openbare accounts bekijken zonder in te loggen!</string>\n    <string name=\"empty_acc\">Dit Account heeft Geen Berichten</string>\n    <string name=\"empty_list\">Geen dergelijke berichten!</string>\n    <string name=\"login\">Aanmelden</string>\n    <string name=\"logout\">Uitloggen</string>\n    <string name=\"logout_summary\">Anoniem Instagram browsen</string>\n    <string name=\"remove_all_acc\">Verwijder alle accounts</string>\n    <string name=\"remove_all_acc_warning\">Dit zal alle toegevoegde accounts van de app verwijderen\\nOm alleen één account te verwijderen, houdt het account lang ingedrukt bij het account-wisselscherm\\nWil je doorgaan?</string>\n    <string name=\"time_settings\">Datumnotatie</string>\n    <string name=\"saved_create_collection\">Nieuwe collectie maken</string>\n    <string name=\"edit_collection\">Verander collectienaam</string>\n    <string name=\"delete_collection\">Verwijder collectie</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Uit collectie verwijderen</string>\n    <string name=\"liked\">Leuk gevonden</string>\n    <string name=\"saved\">Opgeslagen</string>\n    <string name=\"tagged\">Getagd</string>\n    <string name=\"dm_person\">Bericht</string>\n    <string name=\"follow\">Volg</string>\n    <string name=\"unfollow\">Ontvolg</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Toevoegen aan favorieten</string>\n    <string name=\"block\">Blokkeer</string>\n    <string name=\"unblock\">Deblokkeer</string>\n    <string name=\"restrict\">Beperk</string>\n    <string name=\"unrestrict\">De-restrict</string>\n    <string name=\"mute_stories\">Verhalen uitschakelen</string>\n    <string name=\"mute_posts\">Berichten dempen</string>\n    <string name=\"unmute_stories\">Verhalen uitzetten</string>\n    <string name=\"unmute_posts\">Berichten uitschakelen</string>\n    <string name=\"remove_follower\">Volger verwijderen</string>\n    <string name=\"bio_copy\">Bio kopiëren</string>\n    <string name=\"bio_translate\">Bio vertalen</string>\n    <string name=\"status_mutual\">Wederzijds</string>\n    <string name=\"status_following\">Volgt</string>\n    <string name=\"status_follower\">Volger</string>\n    <string name=\"map\">Kaart</string>\n    <string name=\"dialog_export_accounts\">Accounts</string>\n    <string name=\"dialog_export_settings\">Instellingen</string>\n    <string name=\"dialog_export_favorites\">Favorieten</string>\n    <string name=\"dialog_import_success\">Succesvol geïmporteerd!</string>\n    <string name=\"dialog_import_failed\">Importeren mislukt!</string>\n    <string name=\"dialog_export_success\">Succesvol geëxporteerd!</string>\n    <string name=\"dialog_export_failed\">Exporteren mislukt!</string>\n    <string name=\"refresh\">Ververs</string>\n    <string name=\"get_cookies\">Cookies ophalen</string>\n    <string name=\"time_settings_title_custom\">Aangepaste indeling gebruiken</string>\n    <string name=\"time_settings_title_separator\">Scheidingsteken</string>\n    <string name=\"time_settings_title_time_format\">Tijdnotatie</string>\n    <string name=\"time_settings_title_date_format\">Datumindeling</string>\n    <string name=\"time_settings_title_preview\">Voorbeeld</string>\n    <string name=\"time_settings_swap_time\">Wissel tijd- en datumposities</string>\n    <string name=\"quick_access_cannot_delete_curr\">Kan momenteel niet het account verwijderen dat in gebruik is</string>\n    <string name=\"quick_access_confirm_delete\">Weet je zeker dat je \\'%s\\' wilt verwijderen?</string>\n    <string name=\"open_profile\">Open profiel</string>\n    <string name=\"view_story\">Verhaal bekijken</string>\n    <string name=\"view_pfp\">Bekijk profielfoto</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Niet-ondersteund berichttype</string>\n    <string name=\"dms_inbox_unsend\">Maak bericht ongedaan</string>\n    <string name=\"dms_inbox_giphy\">Bekijk op GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s heeft een post gedeeld door @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s heeft een afbeelding gedeeld</string>\n    <string name=\"dms_inbox_shared_video\">%s heeft een video gedeeld</string>\n    <string name=\"dms_inbox_shared_message\">%s heeft een bericht verzonden</string>\n    <string name=\"dms_inbox_shared_gif\">%s heeft een Gif gedeeld</string>\n    <string name=\"dms_inbox_shared_sticker\">%s heeft een sticker gedeeld</string>\n    <string name=\"dms_inbox_shared_profile\">%s heeft een profiel gedeeld: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s deelde een locatie: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s heeft een verhaalmarkering gedeeld door @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s heeft een verhaal gedeeld door @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s heeft een spraakbericht gestuurd</string>\n    <string name=\"dms_inbox_shared_clip\">%s deelde een clip met @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s deelde een IGTV-video van @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Je hebt gereageerd op hun verhaal: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s heeft gereageerd op je verhaal: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Je hebt gereageerd op hun verhaal: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reageerde op je verhaal: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">U heeft @%s genoemd in uw verhaal</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s heeft je genoemd in hun verhaal</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Onbekend mediatype</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media verlopen!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Afgeleverd</string>\n    <string name=\"dms_inbox_raven_media_sent\">Verzonden</string>\n    <string name=\"dms_inbox_raven_media_opened\">Geopend</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Opnieuw afgespeeld</string>\n    <string name=\"dms_inbox_raven_media_sending\">Bezig met verzenden…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Geblokkeerd</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Voorgesteld</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshot genomen</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Kan niet afleveren</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Ingedrukt houden om audio op te nemen</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Verlaat chat</string>\n    <string name=\"dms_action_leave_question\">Deze chat verlaten?</string>\n    <string name=\"dms_action_kick\">Kick</string>\n    <string name=\"dms_left_users\">Gebruikers verlaten</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Ongeldige gebruikersnaam</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram staat niet toe dat video\\'s langer dan 60 seconden worden geüpload voor DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram staat niet toe dat audio langer dan 60 seconden wordt geüpload.</string>\n    <string name=\"direct_download_loading\">Post(s) ophalen</string>\n    <string name=\"downloader_complete\">Download voltooid</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Bericht downloaden…</string>\n    <string name=\"downloader_downloading_media\">Media downloaden</string>\n    <string name=\"downloader_unknown_error\">Er is een onbekende fout opgetreden!!!</string>\n    <string name=\"downloader_error_creating_folder\">Fout bij aanmaken map!</string>\n    <string name=\"downloader_error_download_file\">Fout bij downloaden bestand</string>\n    <string name=\"comment_viewer_translate_comment\">Reactie vertalen</string>\n    <string name=\"comment_viewer_delete_comment\">Verwijder reactie</string>\n    <string name=\"followers_type_followers\">Volgers</string>\n    <string name=\"followers_type_following\">Volgend</string>\n    <string name=\"followers_compare\">Vergelijk volgers &amp; volgend</string>\n    <string name=\"followers_both_following\">Beide volgen elkaar</string>\n    <string name=\"followers_not_following\">niet volgend %s</string>\n    <string name=\"followers_not_follower\">%s is niet aan het volgen</string>\n    <string name=\"login_error_loading_cookies\">Fout bij laden van cookies</string>\n    <string name=\"comment_hint\">Schrijf een nieuwer reactie…</string>\n    <string name=\"liked_notif\">Vond je bericht leuk</string>\n    <string name=\"comment_notif\">Reageerde op je bericht:</string>\n    <string name=\"follow_notif\">Volgt je nu</string>\n    <string name=\"tagged_notif\">Heeft je getagd in een bericht</string>\n    <string name=\"request_notif\">Heeft een volgverzoek ingediend</string>\n    <string name=\"request_approve\">Verzoek accepteren</string>\n    <string name=\"request_reject\">Verzoek afwijzen</string>\n    <string name=\"share_public_post\">Deel dit openbare bericht met…</string>\n    <string name=\"share_private_post\">Dit is een privé bericht! Deel dit met degenen die het kunnen bekijken.</string>\n    <string name=\"discover_empty\">Deze categorie is op de een of andere manier leeg…</string>\n    <string name=\"update_available\">Een update is beschikbaar(%s)</string>\n    <string name=\"updated\">Bedankt voor het bijwerken van Barinsta!</string>\n    <string name=\"crash_title\">App is gecrasht</string>\n    <string name=\"crash_descr\">Oeps.. de app is gecrasht, maar geen zorgen. Je kan een foutrapportage naar de ontwikkelaar sturen om hem te helpen met oplossen. (:</string>\n    <string name=\"action_notif\">Activiteit</string>\n    <string name=\"action_archive\">Verhaalarchief</string>\n    <string name=\"action_ayml\">Voorgestelde gebruikers</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d volgers</string>\n    <string name=\"activity_count_comments\">%d opmerkingen</string>\n    <string name=\"activity_count_commentlikes\">%d opmerking-likes</string>\n    <string name=\"activity_count_usertags\">%d gebruikerstags</string>\n    <string name=\"activity_count_likes\">%d likes</string>\n    <string name=\"activity_count_poy\">%d foto\\'s van jou</string>\n    <string name=\"activity_count_requests\">%d volgverzoeken</string>\n    <string name=\"activity_notloggedin\">Bent u uitgelogd voordat u op deze melding klikt?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profiel</string>\n    <string name=\"more\">Meer</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d geselecteerd</string>\n    <string name=\"logout_success\">Je bent succesvol uitgelogd!</string>\n    <string name=\"dm_thread_info\">Informatie</string>\n    <string name=\"mark_as_seen\">Markeer als gelezen</string>\n    <string name=\"version\">Versie</string>\n    <string name=\"pref_start_screen\">Startscherm</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Toon toetsenbord bij zoeken</string>\n    <string name=\"pref_category_general\">Algemeen</string>\n    <string name=\"pref_category_theme\">Thema</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Lokaal</string>\n    <string name=\"account\">Account</string>\n    <string name=\"account_hint\">Huidige login werkt niet? Voeg gewoon opnieuw het account toe.</string>\n    <string name=\"add_account\">Account toevoegen</string>\n    <string name=\"about_category_license\">Licentie (alleen Engels)</string>\n    <string name=\"about_documentation\">Bezoek onze website</string>\n    <string name=\"about_documentation_summary\">Krijg ondersteuning, discussieer, ontmoet anderen en veel plezier!</string>\n    <string name=\"about_repository\">Bekijk onze broncode op GitHub</string>\n    <string name=\"about_repository_summary\">Audit, ster, meld bugs, bijdragen en veel plezier (nog een keer)!</string>\n    <string name=\"about_feedback\">Stuur feedback per e-mail</string>\n    <string name=\"about_category_3pt\">Attributen van derden</string>\n    <string name=\"reminder\">Herinnering</string>\n    <string name=\"reminder_summary\">Gebruik deze app verantwoordelijk. Gedownloade afbeeldingen mogen alleen worden gebruikt voor doeleinden die zijn toegestaan door toepasselijke wetten.</string>\n    <string name=\"light_white_theme\">Wit</string>\n    <string name=\"dark_black_theme\">Zwart</string>\n    <string name=\"light_theme_settings\">Licht thema</string>\n    <string name=\"dark_theme_settings\">Donker thema</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Materiaal Donker</string>\n    <string name=\"added_to_favs\">Toegevoegd aan favorieten!</string>\n    <string name=\"add_to_favorites\">Voeg toe aan Favorieten</string>\n    <string name=\"accounts\">Accounts</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Locaties</string>\n    <string name=\"unknown\">Onbekend</string>\n    <string name=\"removed_from_favs\">Verwijderd uit Favorieten!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Herstel</string>\n    <string name=\"auto_backup\">Automatische back-up</string>\n    <string name=\"auto_backup_summary\">Vanaf Android 6, zal Android\\'s Auto Backup functie alle app instellingen, account login gegevens, en favorieten uploaden naar uw Google Drive, die kan worden hersteld door het opnieuw installeren van de app na de-installatie.</string>\n    <string name=\"auto_backup_warning\">Deze voorkeur heeft geen effect als Google Play Services niet aanwezig is, of als Auto Backup is uitgeschakeld in uw toestelinstellingen. Als u dit uitschakelt, worden bestaande back-ups niet gewist.</string>\n    <string name=\"auto_backup_setting\">Automatische back-up inschakelen</string>\n    <string name=\"manual_backup\">Handmatige back-up</string>\n    <string name=\"backup_summary\">Back-up maken van Barinsta app-instellingen, accountloggegevens en/of favorieten naar een platte tekst of versleuteld back-up bestand voor later herstellen.</string>\n    <string name=\"backup_warning\">Als u een back-up van accountaanmeldgegevens maakt, het bestand vertrouwelijk behandelt en ergens veilig houdt!</string>\n    <string name=\"create_backup\">Nieuw back-upbestand maken</string>\n    <string name=\"restore_backup\">Herstellen vanuit bestaand back-upbestand</string>\n    <string name=\"file_chosen_label\">Bestand:</string>\n    <string name=\"enter_password\">Voer wachtwoord in</string>\n    <string name=\"select_backup_file\">Backup-bestand selecteren (.zaai/.backup)</string>\n    <string name=\"apply\">Toepassen</string>\n    <string name=\"save\">Opslaan</string>\n    <string name=\"caption\">Bijschrift</string>\n    <string name=\"edit_caption\">Bewerk onderschrift</string>\n    <string name=\"translate_caption\">Vertaal bijschrift</string>\n    <string name=\"player_timeline_desc\">Videospeler tijdlijn</string>\n    <string name=\"liking\">Liken…</string>\n    <string name=\"like_unsuccessful\">Like niet gelukt</string>\n    <string name=\"unlike_unsuccessful\">Unlike niet gelukt</string>\n    <string name=\"unliking\">Unliken…</string>\n    <string name=\"controls\">Besturing</string>\n    <string name=\"saving\">Opslaan…</string>\n    <string name=\"removing\">Verwijderen…</string>\n    <string name=\"save_unsuccessful\">Opslaan mislukt</string>\n    <string name=\"save_remove_unsuccessful\">Verwijderen mislukt</string>\n    <string name=\"downloading\">Downloaden…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d van %2$d</string>\n    <string name=\"delete\">Verwijder</string>\n    <string name=\"comment\">Reageeer</string>\n    <string name=\"layout\">Lay-out</string>\n    <string name=\"feed_stories\">Feed verhalen</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Delen</string>\n    <string name=\"layout_style\">Lay-out stijl</string>\n    <string name=\"column_count\">Aantal kolommen</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Toon namen</string>\n    <string name=\"show_avatars\">Toon avatars</string>\n    <string name=\"avatar_size\">Avatar grootte</string>\n    <string name=\"corners\">Hoeken</string>\n    <string name=\"show_grid_gap\">Rasterruimte weergeven</string>\n    <string name=\"post_not_found\">Bericht niet gevonden!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galerij</string>\n    <string name=\"camera\">Camera</string>\n    <string name=\"all_photos\">Alle Foto\\'s</string>\n    <string name=\"all_media\">Alle Media</string>\n    <string name=\"all_videos\">Alle Video\\'s</string>\n    <string name=\"brightness\">Helderheid</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Levendigheid</string>\n    <string name=\"saturation\">Verzadiging</string>\n    <string name=\"sharpen\">Scherpen</string>\n    <string name=\"exposure\">Blootstelling</string>\n    <string name=\"center\">Centreren</string>\n    <string name=\"color\">Kleur</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">Einde</string>\n    <string name=\"bilateral_blur\">Bilaterale vervaging</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Vak vervagen</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Opnieuw</string>\n    <string name=\"crop\">Knip af</string>\n    <string name=\"normal\">Normaal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d bekijken</item>\n        <item quantity=\"other\">%d bekeken</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s verhaal</item>\n        <item quantity=\"other\">%s verhalen</item>\n    </plurals>\n    <string name=\"details\">Details</string>\n    <string name=\"title\">Titel</string>\n    <string name=\"members\">Members</string>\n    <string name=\"admin\">Beheerder</string>\n    <string name=\"inviter\">Uitnodiger</string>\n    <string name=\"mute_messages\">Berichten dempen</string>\n    <string name=\"mute_mentions\">Demp vermeldingen</string>\n    <string name=\"add_members\">Leden toevoegen</string>\n    <string name=\"search\">Zoeken</string>\n    <string name=\"done\">Gedaan</string>\n    <string name=\"dms_action_make_admin\">Beheerder maken</string>\n    <string name=\"dms_action_remove_admin\">Verwijderen als Admin</string>\n    <string name=\"edit_unsuccessful\">Bewerken is mislukt</string>\n    <string name=\"message\">Bericht</string>\n    <string name=\"tap_to_remove\">Tik om te verwijderen</string>\n    <string name=\"forward\">Doorsturen</string>\n    <string name=\"forward_outgoing\">U heeft een bericht doorgestuurd</string>\n    <string name=\"forward_incoming\">Bericht doorgestuurd</string>\n    <string name=\"add\">Toevoegen</string>\n    <string name=\"send\">Versturen</string>\n    <string name=\"replying_to_yourself\">Antwoorden op jezelf</string>\n    <string name=\"replying_to_user\">Antwoorden op %s</string>\n    <string name=\"replied_to_yourself\">Je antwoordde op jezelf</string>\n    <string name=\"replied_you\">Je antwoordde</string>\n    <string name=\"replied_you_group\">Je antwoordde op %s</string>\n    <string name=\"replied_group\">Geantwoord op %s</string>\n    <string name=\"replied_to_you\">Beantwoord jou</string>\n    <string name=\"replied_to_themself\">Beantwoord aan zichzelf</string>\n    <string name=\"reacted_story_outgoing\">Je hebt gereageerd op hun verhaal</string>\n    <string name=\"reacted_story_incoming\">Reageerde op je verhaal</string>\n    <string name=\"mentioned_story_outgoing\">Je hebt ze genoemd in je verhaal</string>\n    <string name=\"mentioned_story_incoming\">Heeft je genoemd in hun verhaal</string>\n    <string name=\"replied_story_outgoing\">Je hebt gereageerd op hun verhaal</string>\n    <string name=\"replied_story_incoming\">Heeft gereageerd op je verhaal</string>\n    <string name=\"raven_image_expired\">Beeld is verlopen</string>\n    <string name=\"raven_image_info\">Beeld zal verlopen wanneer gezien</string>\n    <string name=\"raven_video_expired\">Video is verlopen</string>\n    <string name=\"raven_video_info\">Video zal verlopen wanneer gezien</string>\n    <string name=\"raven_msg_expired\">Bericht is verlopen</string>\n    <string name=\"raven_msg_info\">Bericht vervalt wanneer gezien</string>\n    <string name=\"story_share\">\\@%s\\'s verhaal</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s verhaal highlight</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Spraakbericht</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">Goedkeuring vereist om toe te treden</string>\n    <string name=\"requests\">Verzoeken</string>\n    <string name=\"admins_only\">Alleen admins</string>\n    <string name=\"added_by\">Toegevoegd door %s</string>\n    <string name=\"admin_approval_required\">Goedkeuring door de beheerder vereist</string>\n    <string name=\"admin_approval_required_description\">Een admin goedkeuring zal nodig zijn om nieuwe leden aan de groep toe te voegen</string>\n    <string name=\"dms_action_end\">Einde chatsessie</string>\n    <string name=\"dms_action_end_question\">Einde chatsessie?</string>\n    <string name=\"dms_action_end_description\">Alle leden zullen uit de groep verwijderd worden. Zij zullen nog steeds de chatgeschiedenis kunnen bekijken.</string>\n    <string name=\"pending_requests\">Hangende aanvragen</string>\n    <string name=\"accept_request_from_user\">Accepteer verzoek van %1s (%2s)?</string>\n    <string name=\"decline\">Geweigerd</string>\n    <string name=\"accept\">Accepteer</string>\n    <string name=\"you\">Jij</string>\n    <string name=\"no_pending_requests\">Geen lopende verzoeken</string>\n    <string name=\"checking_for_new_messages\">Controleren op nieuwe berichten</string>\n    <string name=\"pref_category_stories\">Verhalen</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Meldingen</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">DM meldingen inschakelen</string>\n    <string name=\"enable_dm_auto_refesh\">Berichten automatisch vernieuwen</string>\n    <string name=\"auto_refresh_every\">Automatisch vernieuwen elke</string>\n    <string name=\"secs\">sec</string>\n    <string name=\"mins\">min</string>\n    <string name=\"search_giphy\">Zoek GIPHY</string>\n    <string name=\"generic_null_response\">Antwoord is leeg!</string>\n    <string name=\"generic_not_ok_response\">Antwoord status is niet goed!</string>\n    <string name=\"generic_failed_request\">Verzoek mislukt!</string>\n    <string name=\"hint_keyword\">Sleutelwoord</string>\n    <string name=\"toggle_keyword_filter\">Sleutelwoordfilter inschakelen</string>\n    <string name=\"edit_keyword_filter\">Sleutelwoordfilters bewerken</string>\n    <string name=\"added_keywords\">Sleutelwoord toegevoegd: %s aan filterlijst</string>\n    <string name=\"removed_keywords\">Sleutelwoord verwijderd: %s uit filterlijst</string>\n    <string name=\"marked_as_seen\">Gemarkeerd als gezien</string>\n    <string name=\"delete_unsuccessful\">Verwijderen niet gelukt</string>\n    <string name=\"throttle_error\">Gestreefd door Instagram vanwege te veel API verzoeken. Wacht even voor je opnieuw probeert.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">Deze account is afgemeld.</string>\n    <string name=\"login_required\">Inloggen verplicht!</string>\n    <string name=\"inactive_user\">Gebruiker is inactief!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Rapport</string>\n    <string name=\"crash_report_title\">Selecteer een email app om crashlogs te verzenden</string>\n    <string name=\"not_found\">Niet gevonden!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Deze update overslaan</string>\n    <string name=\"on_latest_version\">Je bent al op de nieuwste versie</string>\n    <string name=\"tab_order\">Scherm volgorde</string>\n    <string name=\"other_tabs\">Andere tabbladen</string>\n    <string name=\"tab_order_start_next_launch\">De tabvolgorde zal weerspiegeld worden bij de volgende start</string>\n    <string name=\"dm_remove_warning\">Indien opgeslagen, zullen alle DM gerelateerde functies worden uitgeschakeld bij volgende start</string>\n    <string name=\"copy_caption\">Kopieer opschrift</string>\n    <string name=\"copy_reply\">Antwoord kopiëren</string>\n    <string name=\"restore\">Herstel</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Selecteer een map waar Barinsta downloads en tijdelijke bestanden kan opslaan.\\n\\nJe kunt dit later wijzigen in meer &gt; Instellingen &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android heeft de manier veranderd waarop apps toegang hebben tot bestanden en mappen op de opslag. Op dit moment heeft Barinsta geen toestemming om toegang te krijgen tot de volgende map:</string>\n    <string name=\"dir_select_permission_revoked_message\">De machtigingen voor de eerder geselecteerde map zijn door het systeem ingetrokken:</string>\n    <string name=\"dir_select_folder_not_exist\">De eerder geselecteerde map bestaat nu niet meer:</string>\n    <string name=\"dir_select_message2\">Kies de directory opnieuw of kies een nieuwe directory door op de knop hieronder te klikken.</string>\n    <string name=\"select_a_folder\">Geen map geselecteerd!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Succes! Even geduld aub. Start app…</string>\n    <string name=\"barinsta_folder\">Barinsta map</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Wissen</string>\n    <string name=\"no_external_map_app\">Geen Kaartapp gevonden!</string>\n    <string name=\"click_to_show_full\">Klik om volledige like count te tonen</string>\n    <string name=\"no_profile_pic_found\">Geen profielfoto gevonden!</string>\n    <string name=\"swipe_up_confirmation\">Ben je zeker dat je deze link wilt openen?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Delen via DM</string>\n    <string name=\"share_link\">Link delen…</string>\n    <string name=\"slide_to_cancel\">Schuif om te annuleren</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-or/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>ସିଷ୍ଟମ ନିର୍ଧାରିତ</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>ସ୍ୱୟଂକ୍ରିୟ /ସିସ୍ଟମ ଅନୁସାରେ</item>\n        <item>ସ୍ୱୟଂକ୍ରିୟ /ବ୍ୟାଟେରୀ ଅନୁସାରେ</item>\n        <item>ଗାଢ</item>\n        <item>ହାଲୁକା</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>ଇନଷ୍ଟାଗ୍ରାମ ଅନୁଯାୟୀ ‌(ପ୍ରଥମେ ଦେଖି ନ ଥିବା ଓ ପରେ ଦେଖିଥିବା)</item>\n        <item>ନୂତନ ରୁ ପୁରାତନ</item>\n        <item>ପୁରାତନ ରୁ ନୂତନ</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>କିଛି ନୁହେଁ</item>\n        <item>\\@</item>\n        <item>ଠାରେ</item>\n        <item></item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>ସେକେଣ୍ଡ</item>\n        <item>ମିନିଟ୍</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-or/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">ଆମ ବିଷୟରେ</string>\n    <string name=\"action_dms\">ବାର୍ତାଳାପ</string>\n    <string name=\"action_settings\">ସେଟିଂସ (ବିକଳ୍ପ ଗୁଡିକ)</string>\n    <string name=\"action_download\">ଡାଉନଲୋଡ୍ କରନ୍ତୁ</string>\n    <string name=\"action_search\">ବ୍ୟବହାରକର୍ତା ନାମ ଖୋଜ…</string>\n    <string name=\"action_compare\">ତୁଳନା କର</string>\n    <string name=\"clipboard_error\">ଲେଖା କପି କରିବାରେ ତ୍ରୁଟି</string>\n    <string name=\"clipboard_copied\">କ୍ଲିପବୋର୍ଡରେ କପି କରାଗଲା!</string>\n    <string name=\"report\">ରିପୋର୍ଟ କରନ୍ତୁ</string>\n    <string name=\"set_password\">ପାସୱାର୍ଡ ଦ୍ବାରା ଫାଇଲକୁ ସୁରକ୍ଷିତ କରନ୍ତୁ</string>\n    <string name=\"password_no_max\">ପାସୱାର୍ଡ</string>\n    <string name=\"ok\">ଠିକ୍ ଅଛି</string>\n    <string name=\"yes\">ହଁ</string>\n    <string name=\"cancel\">ରଦ୍ଦ କର</string>\n    <string name=\"no\">ନାଁ</string>\n    <string name=\"confirm\">ନିଶ୍ଚିତ କରନ୍ତୁ</string>\n    <string name=\"title_favorites\">ପସନ୍ଦିତ</string>\n    <string name=\"title_discover\">ଖୋଜିବା</string>\n    <string name=\"title_comments\">ଟିପ୍ପଣୀ</string>\n    <string name=\"title_replies\">ଟିପ୍ପଣୀଗୁଡିକ</string>\n    <string name=\"title_notifications\">କାର୍ଯ୍ୟକଳାପ</string>\n    <string name=\"update_check\">ଖୋଲିବା ସମୟରେ ଅପଡେଟ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ</string>\n    <string name=\"flag_secure\">ସ୍କ୍ରିନସଟ୍ ଏବଂ app preview ଅବରୋଧ କରନ୍ତୁ</string>\n    <string name=\"download_user_folder\">ଡାଉନଲୋଡ ପୋଷ୍ଟକୁ ବ୍ୟବହାରକାରୀଙ୍କ ନାମରେ ହୋଇଥିବା ସ୍ଥାନ ରେ ରଖ</string>\n    <string name=\"download_prepend_username\">ଏକାଉଣ୍ଟ ନାମକୁ ଫାଇଲନାମ ସହିତ ଯୋଡନ୍ତୁ</string>\n    <string name=\"mark_as_seen_setting\">କାହାଣୀଗୁଡିକ ଦେଖିବା ପରେ \\'ଦେଖାଗଲା\\' ଚିହ୍ନିତ କରନ୍ତୁ |</string>\n    <string name=\"mark_as_seen_setting_summary\">କାହାଣୀ ପ୍ରେରକ ଜାଣିବେ ତୁମେ ଏହାକୁ ଦେଖିଛ</string>\n    <string name=\"hide_muted_reels_setting\">ଫିଡ୍ ରୁ mute ହୋଇଥିବା କାହାଣୀ କୁ ବାଦ ଦିଅନ୍ତୁ</string>\n    <string name=\"dm_mark_as_seen_setting\">ବାର୍ତା ଦେଖିବା ପରେ \\'ଦେଖାଗଲା\\' ଚିହ୍ନିତ କରନ୍ତୁ |</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">ଅନ୍ୟ ସଦସ୍ୟମାନେ ଜାଣିବେ ତୁମେ ଏହାକୁ ଦେଖିଛ।</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">କାର୍ଯ୍ୟକଳାପ ସୂଚନା ଦେଖାନ୍ତୁ</string>\n    <string name=\"story_sort_setting\">କାହାଣୀଗୁଡିକ ଶ୍ରେଣୀବଦ୍ଧ କରନ୍ତୁ</string>\n    <string name=\"error_loading_profile\">ପ୍ରୋଫାଇଲ୍ ଲୋଡ୍ କରିବାରେ ତ୍ରୁଟି! ଉପଯୋଗକର୍ତ୍ତା ନାମ ସଠିକ କି? ଯଦି ଠିକ ଅଛି, ତେବେ ଆପଣ instagram ଦ୍ବାରା ସୀମିତ ହୋଇପାରନ୍ତି |</string>\n    <string name=\"error_loading_hashtag\">ହ୍ୟାସଟ୍ୟାଗ୍ ଲୋଡ୍ କରିବାରେ ତ୍ରୁଟି! ନାମ ଠିକ୍ ତ?</string>\n    <string name=\"error_loading_location\">ସ୍ଥାନ ଲୋଡ୍ କରିବାରେ ତ୍ରୁଟି! ଲିଙ୍କ୍ ଠିକ୍ ତ?</string>\n    <string name=\"error_creating_folders\">ଡ଼ାଉନଲୋଡ଼ ଫୋଲଡ଼ର ସୃଷ୍ଟି କରିବାରେ ତ୍ରୁଟି ପରିଲକ୍ଷିତ ହେଉଛି।</string>\n    <string name=\"select_folder\">ଫୋଲ୍‌ଡର୍‌ ଚୟନ କରନ୍ତୁ</string>\n    <string name=\"theme_settings\">ଥିମ</string>\n    <string name=\"select_language\">ଭାଷା</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\n ପୋଷ୍ଟ</item>\n        <item quantity=\"other\">%s\\nପୋଷ୍ଟସ</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s ପୋଷ୍ଟ</item>\n        <item quantity=\"other\">%s ପୋଷ୍ଟସ</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nFollower</item>\n        <item quantity=\"other\">%s\\nଅନୁଗାମୀଗଣ</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nଅନୁସରଣ କରୁଛନ୍ତି</string>\n    <string name=\"post_viewer_autoplay_video\">ଭିଡ଼ିଓ ସ୍ୱତଃ ଚାଲୁ କର</string>\n    <string name=\"post_viewer_background_play\">ପ୍ରଚ୍ଛଦପଟରେ ଭିଡିଓ ଜାରି ରଖନ୍ତୁ |</string>\n    <string name=\"post_viewer_background_play_summary\">ଆପ୍ ଫୋକସ୍ ବାହାରେ ଥିବାବେଳେ ଭିଡିଓଗୁଡିକୁ pause କରନ୍ତୁ ନାହିଁ |</string>\n    <string name=\"post_viewer_muted_autoplay\">ସର୍ବଦା ଭିଡ଼ିଓକୁ ଶବ୍ଦହୀନ ରଖ</string>\n    <string name=\"post_viewer_show_captions\">ସର୍ବଦା ପୋଷ୍ଟ କ୍ୟାପସନ୍ ଦେଖାନ୍ତୁ |</string>\n    <string name=\"post_viewer_download_dialog_title\">ଡାଉନଲୋଡ଼ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ</string>\n    <string name=\"post_viewer_download_current\">ସମ୍ପ୍ରତି</string>\n    <string name=\"post_viewer_download_album\">ସମସ୍ତ ଆଲବମ</string>\n    <string name=\"show_stories\">କାହାଣୀଗୁଡିକ ଦେଖାନ୍ତୁ</string>\n    <string name=\"no_more_stories\">ଆଉ କାହାଣୀ ନାହିଁ!</string>\n    <string name=\"view_post\">ପୋଷ୍ଟ ଦେଖନ୍ତୁ</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">ଉତ୍ତର ସଫଳ!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d ପ୍ରତିକ୍ରିୟା ହାରାହାରି %s</item>\n        <item quantity=\"other\">%d ପ୍ରତିକ୍ରିୟାଗୁଡିକ ହାରାହାରି %s</item>\n    </plurals>\n    <string name=\"slider_answer\">ଆପଣଙ୍କ ଉତ୍ତର: %s</string>\n    <string name=\"reply_story\">କାହାଣୀକୁ ଜବାବ ଦିଅ |</string>\n    <string name=\"reply_hint\">ଜବାବ…</string>\n    <string name=\"story_quiz\">ପ୍ରଶ୍ନୋତ୍ତର</string>\n    <string name=\"story_slider\">ସ୍ଲାଇଡର୍</string>\n    <string name=\"story_quizzed\">ଆପଣ ଉତ୍ତର ଦେଇସାରିଛନ୍ତି!</string>\n    <string name=\"story_mentions\">ଉଲ୍ଲେଖଗୁ‌‍‌‌‌‌‌‌‌‌‌‌‌ଡିକ</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">ଏହି ଏକାଉଣ୍ଟ ଗୁପ୍ତ ଅଟେ</string>\n    <string name=\"priv_acc_confirm\">ଆପଣ ଅନୁସରଣ ନ କଲେ ପୋଷ୍ଟଗୁଡିକୁ ପ୍ରବେଶ କରିବାକୁ ସମର୍ଥ ହେବେ ନାହିଁ! ଆପଣ ନିଶ୍ଚିତ କି?</string>\n    <string name=\"are_you_sure\">ଆପଣ ନିଶ୍ଚିତ ତ?</string>\n    <string name=\"no_acc\">ଆପଣ ନିମ୍ନ - ଡାହାଣ କୋଣରେ ଅଧିକ -&gt; ଆକାଉଣ୍ଟ୍ ମାଧ୍ୟମରେ ଲଗ୍ ଇନ୍ କରିପାରିବେ କିମ୍ବା ଆପଣ ଲଗ୍ଇନ୍ ବିନା ସର୍ବସାଧାରଣ ଆକାଉଣ୍ଟ୍ ଦେଖିପାରିବେ |!</string>\n    <string name=\"empty_acc\">ଏହି ଆକାଉଣ୍ଟରେ କୌଣସି ପୋଷ୍ଟ ନାହିଁ |</string>\n    <string name=\"empty_list\">ଏପରି କୌଣସି ପୋଷ୍ଟ ନାହିଁ!</string>\n    <string name=\"login\">ଲଗ ଇନ୍</string>\n    <string name=\"logout\">ଲଗ ଆଉଟ୍</string>\n    <string name=\"logout_summary\">ବିନା ଏକାଉଣ୍ଟରେ ଇନଷ୍ଟାଗ୍ରାମ ବ୍ରାଉଜ୍ କରନ୍ତୁ |</string>\n    <string name=\"remove_all_acc\">ସମସ୍ତ ଏକାଉଣ୍ଟ ହଟାନ୍ତୁ</string>\n    <string name=\"remove_all_acc_warning\">ଏହା ଆପରୁ ସମସ୍ତ ଯୋଡା ଯାଇଥିବା ଖାତାଗୁଡ଼ିକୁ ଅପସାରଣ କରିବ! \\n କେବଳ ଗୋଟିଏ ଖାତା ଅପସାରଣ କରିବା ପାଇଁ ଆକାଉଣ୍ଟ୍ ସୁଇଚର୍ ରୁ ଆକାଉଣ୍ଟକୁ ଲମ୍ବା ଟ୍ୟାପ୍ କରନ୍ତୁ |\\nଆପଣ ସମସ୍ତ ଯୋଡା ଯାଇଥିବା ଖାତାଗୁଡ଼ିକୁ ଅପସାରଣ କରିବାକୁ ନିଶ୍ଚିତ ତ?</string>\n    <string name=\"time_settings\">ଦିନାଙ୍କ ସ୍ୱରୂପ</string>\n    <string name=\"saved_create_collection\">ନୂତନ ସଂଗ୍ରହ ସୃଷ୍ଟି କରନ୍ତୁ</string>\n    <string name=\"edit_collection\">ସଂଗ୍ରହଟିର ନାମ ବଦଳାନ୍ତୁ</string>\n    <string name=\"delete_collection\">ସଂଗ୍ରହ ହଟାନ୍ତୁ</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">ସଂଗ୍ରହରୁ କାଢନ୍ତୁ</string>\n    <string name=\"liked\">ପସନ୍ଦ କରିଛନ୍ତି</string>\n    <string name=\"saved\">ସଞ୍ଚୟ ହେଲା</string>\n    <string name=\"tagged\">ଟ୍ୟାଗ୍ କରିଛନ୍ତି</string>\n    <string name=\"dm_person\">ସନ୍ଦେଶ</string>\n    <string name=\"follow\">ଫୋଲୋ କରନ୍ତୁ</string>\n    <string name=\"unfollow\">ଅନୁସରଣ କରନ୍ତୁ ନାହିଁ</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">ପସନ୍ଦିତ</string>\n    <string name=\"block\">ଅବରୋଧ କରନ୍ତୁ |</string>\n    <string name=\"unblock\">ଅବରୋଧ ହଟାନ୍ତୁ</string>\n    <string name=\"restrict\">Restrict</string>\n    <string name=\"unrestrict\">Unrestrict</string>\n    <string name=\"mute_stories\">Mute stories</string>\n    <string name=\"mute_posts\">Mute posts</string>\n    <string name=\"unmute_stories\">Unmute stories</string>\n    <string name=\"unmute_posts\">Unmute posts</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Copy bio</string>\n    <string name=\"bio_translate\">Translate bio</string>\n    <string name=\"status_mutual\">Mutual</string>\n    <string name=\"status_following\">Following</string>\n    <string name=\"status_follower\">Follower</string>\n    <string name=\"map\">Map</string>\n    <string name=\"dialog_export_accounts\">Accounts</string>\n    <string name=\"dialog_export_settings\">Settings</string>\n    <string name=\"dialog_export_favorites\">Favorites</string>\n    <string name=\"dialog_import_success\">Successfully imported!</string>\n    <string name=\"dialog_import_failed\">Failed to import!</string>\n    <string name=\"dialog_export_success\">Successfully exported!</string>\n    <string name=\"dialog_export_failed\">Failed to export!</string>\n    <string name=\"refresh\">Refresh</string>\n    <string name=\"get_cookies\">Get cookies</string>\n    <string name=\"time_settings_title_custom\">Use custom format</string>\n    <string name=\"time_settings_title_separator\">Separator</string>\n    <string name=\"time_settings_title_time_format\">Time Format</string>\n    <string name=\"time_settings_title_date_format\">Date Format</string>\n    <string name=\"time_settings_title_preview\">Preview</string>\n    <string name=\"time_settings_swap_time\">Swap Time and Date positions</string>\n    <string name=\"quick_access_cannot_delete_curr\">Cannot delete currently in use account</string>\n    <string name=\"quick_access_confirm_delete\">Are you sure you want to delete \\'%s\\'?</string>\n    <string name=\"open_profile\">Open profile</string>\n    <string name=\"view_story\">View story</string>\n    <string name=\"view_pfp\">View profile picture</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Unsupported message type</string>\n    <string name=\"dms_inbox_unsend\">Unsend message</string>\n    <string name=\"dms_inbox_giphy\">View on GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unknown media type</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media expired!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Delivered</string>\n    <string name=\"dms_inbox_raven_media_sent\">Sent</string>\n    <string name=\"dms_inbox_raven_media_opened\">Opened</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Replayed</string>\n    <string name=\"dms_inbox_raven_media_sending\">Sending…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blocked</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggested</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshotted</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Cannot deliver</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Press and hold to record audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Leave chat</string>\n    <string name=\"dms_action_leave_question\">Leave this chat?</string>\n    <string name=\"dms_action_kick\">Kick</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Fetching post(s)</string>\n    <string name=\"downloader_complete\">Download completed</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Downloading post…</string>\n    <string name=\"downloader_downloading_media\">Downloading media</string>\n    <string name=\"downloader_unknown_error\">Unknown error occurred!!!</string>\n    <string name=\"downloader_error_creating_folder\">Error creating folder!</string>\n    <string name=\"downloader_error_download_file\">Error downloading file</string>\n    <string name=\"comment_viewer_translate_comment\">Translate comment</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Followers</string>\n    <string name=\"followers_type_following\">Following</string>\n    <string name=\"followers_compare\">Comparing followers &amp; following</string>\n    <string name=\"followers_both_following\">Both following each other</string>\n    <string name=\"followers_not_following\">not following %s</string>\n    <string name=\"followers_not_follower\">%s is not following</string>\n    <string name=\"login_error_loading_cookies\">Error loading cookies</string>\n    <string name=\"comment_hint\">Write a new comment…</string>\n    <string name=\"liked_notif\">Liked your post</string>\n    <string name=\"comment_notif\">Commented on your post:</string>\n    <string name=\"follow_notif\">Started following you</string>\n    <string name=\"tagged_notif\">Tagged you in a post</string>\n    <string name=\"request_notif\">Requested following you</string>\n    <string name=\"request_approve\">Approve request</string>\n    <string name=\"request_reject\">Reject request</string>\n    <string name=\"share_public_post\">Share this public post to…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">This category is somehow empty…</string>\n    <string name=\"update_available\">An update is available! (%s)</string>\n    <string name=\"updated\">Thank you for updating Barinsta!</string>\n    <string name=\"crash_title\">App crashed</string>\n    <string name=\"crash_descr\">Oops.. the app crashed, but don\\'t worry you can send error report to the developer to help him fix the issue. (:</string>\n    <string name=\"action_notif\">Activity</string>\n    <string name=\"action_archive\">Story archive</string>\n    <string name=\"action_ayml\">Suggested users</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d follows</string>\n    <string name=\"activity_count_comments\">%d comments</string>\n    <string name=\"activity_count_commentlikes\">%d comment likes</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d likes</string>\n    <string name=\"activity_count_poy\">%d photos of you</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Profile</string>\n    <string name=\"more\">More</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d selected</string>\n    <string name=\"logout_success\">Successfully logged out!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Mark as seen</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Start screen</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">General</string>\n    <string name=\"pref_category_theme\">Theme</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">Account</string>\n    <string name=\"account_hint\">Current login not working? Simply add the account again.</string>\n    <string name=\"add_account\">Add account</string>\n    <string name=\"about_category_license\">License (English only)</string>\n    <string name=\"about_documentation\">Visit our website</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">See our source code on GitHub</string>\n    <string name=\"about_repository_summary\">Audit, star, report bugs, contribute, and have fun (again)!</string>\n    <string name=\"about_feedback\">Send feedback by email</string>\n    <string name=\"about_category_3pt\">Third-Party Attributions</string>\n    <string name=\"reminder\">Reminder</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">White</string>\n    <string name=\"dark_black_theme\">Black</string>\n    <string name=\"light_theme_settings\">Light theme</string>\n    <string name=\"dark_theme_settings\">Dark theme</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Added to Favorites!</string>\n    <string name=\"add_to_favorites\">Add to Favorites</string>\n    <string name=\"accounts\">Accounts</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Locations</string>\n    <string name=\"unknown\">Unknown</string>\n    <string name=\"removed_from_favs\">Removed from Favourites!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Restore</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">File:</string>\n    <string name=\"enter_password\">Enter password</string>\n    <string name=\"select_backup_file\">Select a backup file (.zaai/.backup)</string>\n    <string name=\"apply\">Apply</string>\n    <string name=\"save\">Save</string>\n    <string name=\"caption\">Caption</string>\n    <string name=\"edit_caption\">Edit caption</string>\n    <string name=\"translate_caption\">Translate caption</string>\n    <string name=\"player_timeline_desc\">Video player timeline</string>\n    <string name=\"liking\">Liking…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">Controls</string>\n    <string name=\"saving\">Saving…</string>\n    <string name=\"removing\">Removing…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">Downloading…</string>\n    <string name=\"downloader_downloading_child\">Download item %1$d of %2$d</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"comment\">Comment</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Share</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Show names</string>\n    <string name=\"show_avatars\">Show avatars</string>\n    <string name=\"avatar_size\">Avatar size</string>\n    <string name=\"corners\">Corners</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">Post not found!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Gallery</string>\n    <string name=\"camera\">Camera</string>\n    <string name=\"all_photos\">All Photos</string>\n    <string name=\"all_media\">All Media</string>\n    <string name=\"all_videos\">All Videos</string>\n    <string name=\"brightness\">Brightness</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exposure</string>\n    <string name=\"center\">Center</string>\n    <string name=\"color\">Color</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">End</string>\n    <string name=\"bilateral_blur\">Bilateral Blur</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Reset</string>\n    <string name=\"crop\">Crop</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d view</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">Details</string>\n    <string name=\"title\">Title</string>\n    <string name=\"members\">Members</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Inviter</string>\n    <string name=\"mute_messages\">Mute messages</string>\n    <string name=\"mute_mentions\">Mute mentions</string>\n    <string name=\"add_members\">Add members</string>\n    <string name=\"search\">Search</string>\n    <string name=\"done\">Done</string>\n    <string name=\"dms_action_make_admin\">Make Admin</string>\n    <string name=\"dms_action_remove_admin\">Remove as Admin</string>\n    <string name=\"edit_unsuccessful\">Edit was unsuccessful</string>\n    <string name=\"message\">Message</string>\n    <string name=\"tap_to_remove\">Tap to remove</string>\n    <string name=\"forward\">Forward</string>\n    <string name=\"forward_outgoing\">You forwarded a message</string>\n    <string name=\"forward_incoming\">Forwarded a message</string>\n    <string name=\"add\">Add</string>\n    <string name=\"send\">Send</string>\n    <string name=\"replying_to_yourself\">Replying to yourself</string>\n    <string name=\"replying_to_user\">Replying to %s</string>\n    <string name=\"replied_to_yourself\">You replied to yourself</string>\n    <string name=\"replied_you\">You replied</string>\n    <string name=\"replied_you_group\">You replied to %s</string>\n    <string name=\"replied_group\">Replied to %s</string>\n    <string name=\"replied_to_you\">Replied to you</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">You reacted to their story</string>\n    <string name=\"reacted_story_incoming\">Reacted to your story</string>\n    <string name=\"mentioned_story_outgoing\">You mentioned them in your story</string>\n    <string name=\"mentioned_story_incoming\">Mentioned you in their story</string>\n    <string name=\"replied_story_outgoing\">You replied to their story</string>\n    <string name=\"replied_story_incoming\">Replied to your story</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Photo</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Voice message</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">Approval required to join</string>\n    <string name=\"requests\">Requests</string>\n    <string name=\"admins_only\">Admins only</string>\n    <string name=\"added_by\">Added by %s</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Decline</string>\n    <string name=\"accept\">Accept</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Notifications</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">Enable DM notifications</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">secs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Search GIPHY</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pl/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Język urządzenia</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Auto / Ustawienia systemu</item>\n        <item>Auto / Ustawienia oszczędzania baterii</item>\n        <item>Ciemny</item>\n        <item>Jasny</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Domyślne ustawienia</item>\n        <item>Od najnowszych do najstarszych</item>\n        <item>Od najstarszych do najnowszych</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Brak</item>\n        <item>\\@</item>\n        <item>o</item>\n        <item>dnia</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>s.</item>\n        <item>min.</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">O aplikacji</string>\n    <string name=\"action_dms\">Prywatne wiadomości</string>\n    <string name=\"action_settings\">Ustawienia</string>\n    <string name=\"action_download\">Pobierz</string>\n    <string name=\"action_search\">Szukaj użytkownika…</string>\n    <string name=\"action_compare\">Porównaj</string>\n    <string name=\"clipboard_error\">Wystąpił błąd podczas kopiowania tekstu</string>\n    <string name=\"clipboard_copied\">Skopiowano do schowka!</string>\n    <string name=\"report\">Zgłoś</string>\n    <string name=\"set_password\">Zabezpiecz plik hasłem</string>\n    <string name=\"password_no_max\">Hasło</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Tak</string>\n    <string name=\"cancel\">Anuluj</string>\n    <string name=\"no\">Nie</string>\n    <string name=\"confirm\">Potwierdź</string>\n    <string name=\"title_favorites\">Ulubione</string>\n    <string name=\"title_discover\">Odkrywaj</string>\n    <string name=\"title_comments\">Komentarze</string>\n    <string name=\"title_replies\">Odpowiedzi</string>\n    <string name=\"title_notifications\">Aktywność</string>\n    <string name=\"update_check\">Sprawdź aktualizacje przy starcie</string>\n    <string name=\"flag_secure\">Blokuj zrzuty ekranu &amp; podgląd aplikacji</string>\n    <string name=\"download_user_folder\">Pobierz posty do folderów o nazwie użytkownika</string>\n    <string name=\"download_prepend_username\">Dodaj nazwę użytkownika do nazwy pliku</string>\n    <string name=\"mark_as_seen_setting\">Oznacz relacje jako widoczne po wyświetleniu</string>\n    <string name=\"mark_as_seen_setting_summary\">Autor relacji będzie widział, że to wyświetliłeś</string>\n    <string name=\"hide_muted_reels_setting\">Ukryj wyciszone relacje z kanału</string>\n    <string name=\"dm_mark_as_seen_setting\">Oznacz wiadomość jako przeczytaną</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Inni użytkownicy będą wiedzieli, że to wyświetliłeś</string>\n    <string name=\"autoplay_stories_setting\">Automatyczne odtwarzanie plików wideo</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Włącz powiadomienia o aktywności</string>\n    <string name=\"story_sort_setting\">Sortowanie relacji</string>\n    <string name=\"error_loading_profile\">Błąd ładowania profilu! Czy nazwa użytkownika jest prawidłowa? Jeśli tak, możesz być ograniczony.</string>\n    <string name=\"error_loading_hashtag\">Błąd ładowania hashtaga! Czy nazwa jest prawidłowa?</string>\n    <string name=\"error_loading_location\">Błąd ładowania lokalizacji! Czy adres URL jest prawidłowy?</string>\n    <string name=\"error_creating_folders\">Błąd podczas tworzenia folderu(ów) pobierania.</string>\n    <string name=\"select_folder\">Wybierz folder</string>\n    <string name=\"theme_settings\">Motyw</string>\n    <string name=\"select_language\">Język</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\npost</item>\n        <item quantity=\"few\">%s\\npostów</item>\n        <item quantity=\"many\">%s\\npostów</item>\n        <item quantity=\"other\">%s\\npostów</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s post</item>\n        <item quantity=\"few\">%s postów</item>\n        <item quantity=\"many\">%s postów</item>\n        <item quantity=\"other\">%s postów</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nobserwujący</item>\n        <item quantity=\"few\">%s\\nobserwujących</item>\n        <item quantity=\"many\">%s\\nobserwujących</item>\n        <item quantity=\"other\">%s\\nobserwujących</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nobserwowanych</string>\n    <string name=\"post_viewer_autoplay_video\">Automatyczne odtwarzanie filmów</string>\n    <string name=\"post_viewer_background_play\">Kontynuuj wideo w tle</string>\n    <string name=\"post_viewer_background_play_summary\">Nie wstrzymuj filmów, gdy aplikacja jest nieaktywna</string>\n    <string name=\"post_viewer_muted_autoplay\">Zawsze wyciszaj filmy</string>\n    <string name=\"post_viewer_show_captions\">Zawsze pokazuj napisy postów</string>\n    <string name=\"post_viewer_download_dialog_title\">Wybierz, co chcesz pobrać</string>\n    <string name=\"post_viewer_download_current\">Bieżący</string>\n    <string name=\"post_viewer_download_album\">Cały album</string>\n    <string name=\"show_stories\">Pokazuj relacje</string>\n    <string name=\"no_more_stories\">Nie ma więcej relacji!</string>\n    <string name=\"view_post\">Zobacz post</string>\n    <string name=\"story_poll\">Ankieta</string>\n    <string name=\"answered_story\">Odpowiedziano pomyślnie!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d odpowiedź średnio %s</item>\n        <item quantity=\"few\">%d odpowiedzi średnio %s</item>\n        <item quantity=\"many\">%d odpowiedzi średnio %s</item>\n        <item quantity=\"other\">%d odpowiedzi średnio %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Twoja odpowiedź: %s</string>\n    <string name=\"reply_story\">Odpowiedz na relację</string>\n    <string name=\"reply_hint\">Odpowiedź…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Suwak</string>\n    <string name=\"story_quizzed\">Już odpowiedziałeś!</string>\n    <string name=\"story_mentions\">Wzmianki</string>\n    <string name=\"story_question\">Pytanie</string>\n    <string name=\"priv_acc\">To konto jest prywatne</string>\n    <string name=\"priv_acc_confirm\">Nie będziesz mieć dostępu do postów po anulowaniu obserwowania! Jesteś pewny?</string>\n    <string name=\"are_you_sure\">Jesteś pewien?</string>\n    <string name=\"no_acc\">Możesz zalogować się za pomocą \\\"Więcej\\\" - &gt; \\\"Konto\\\" w prawym dolnym rogu lub możesz zobaczyć konta publiczne bez logowania!</string>\n    <string name=\"empty_acc\">To konto nie zawiera postów</string>\n    <string name=\"empty_list\">Nie ma więcej postów!</string>\n    <string name=\"login\">Zaloguj</string>\n    <string name=\"logout\">Wyloguj</string>\n    <string name=\"logout_summary\">Przeglądaj Instagram anonimowo</string>\n    <string name=\"remove_all_acc\">Usuń wszystkie konta</string>\n    <string name=\"remove_all_acc_warning\">Spowoduje to usunięcie wszystkich dodanych kont z aplikacji!\\nAby usunąć tylko jedno konto, naciśnij i przytrzymaj konto z okna przełączania kont.\\nCzy chcesz kontynuować?</string>\n    <string name=\"time_settings\">Format daty</string>\n    <string name=\"saved_create_collection\">Utwórz nową kolekcję</string>\n    <string name=\"edit_collection\">Wpisz nazwę kolekcji</string>\n    <string name=\"delete_collection\">Usunąć kolekcję</string>\n    <string name=\"delete_collection_note\">Wszystkie posty zawarte w usuniętej kolekcji pozostaną w innych kolekcjach.</string>\n    <string name=\"add_to_collection\">Dodać do kolekcji…</string>\n    <string name=\"remove_from_collection\">Usuń z kolekcji</string>\n    <string name=\"liked\">Polubione</string>\n    <string name=\"saved\">Zapisane</string>\n    <string name=\"tagged\">Oznaczono</string>\n    <string name=\"dm_person\">Message</string>\n    <string name=\"follow\">Obserwuj</string>\n    <string name=\"unfollow\">Przestań obserwować</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Ulubiony</string>\n    <string name=\"block\">Zablokuj</string>\n    <string name=\"unblock\">Odblokuj</string>\n    <string name=\"restrict\">Ogranicz</string>\n    <string name=\"unrestrict\">Nieograniczone</string>\n    <string name=\"mute_stories\">Wycisz relacje</string>\n    <string name=\"mute_posts\">Wycisz posty</string>\n    <string name=\"unmute_stories\">Wyłącz wyciszenie relacji</string>\n    <string name=\"unmute_posts\">Wyłącz wyciszenie postów</string>\n    <string name=\"remove_follower\">Usuń obserwującego</string>\n    <string name=\"bio_copy\">Skopiuj bio</string>\n    <string name=\"bio_translate\">Przetłumacz bio</string>\n    <string name=\"status_mutual\">Wspólne</string>\n    <string name=\"status_following\">Obserwowane</string>\n    <string name=\"status_follower\">Obserwujący</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Konta</string>\n    <string name=\"dialog_export_settings\">Ustawienia</string>\n    <string name=\"dialog_export_favorites\">Ulubione</string>\n    <string name=\"dialog_import_success\">Zaimportowano pomyślnie!</string>\n    <string name=\"dialog_import_failed\">Importowanie nie powiodło się!</string>\n    <string name=\"dialog_export_success\">Wyeksportowano pomyślnie!</string>\n    <string name=\"dialog_export_failed\">Eksportowanie nie powiodło się!</string>\n    <string name=\"refresh\">Odśwież</string>\n    <string name=\"get_cookies\">Pobierz ciasteczka</string>\n    <string name=\"time_settings_title_custom\">Niestandardowy format</string>\n    <string name=\"time_settings_title_separator\">Separator</string>\n    <string name=\"time_settings_title_time_format\">Format czasu</string>\n    <string name=\"time_settings_title_date_format\">Format daty</string>\n    <string name=\"time_settings_title_preview\">Podgląd</string>\n    <string name=\"time_settings_swap_time\">Zamień pozycją czas i datę</string>\n    <string name=\"quick_access_cannot_delete_curr\">Nie można usunąć aktualnie używanego konta</string>\n    <string name=\"quick_access_confirm_delete\">Czy na pewno chcesz usunąć \\'%s\\'?</string>\n    <string name=\"open_profile\">Otwórz profil</string>\n    <string name=\"view_story\">Zobacz relację</string>\n    <string name=\"view_pfp\">Wyświetl zdjęcie profilowe</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Nieobsługiwany typ wiadomości</string>\n    <string name=\"dms_inbox_unsend\">Cofnij wysłanie wiadomości</string>\n    <string name=\"dms_inbox_giphy\">Zobacz na GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s udostępnił post @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s udostępnił obraz</string>\n    <string name=\"dms_inbox_shared_video\">%s udostępnił film</string>\n    <string name=\"dms_inbox_shared_message\">%s wysłał wiadomość</string>\n    <string name=\"dms_inbox_shared_gif\">%s udostępnił gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s udostępnił naklejkę</string>\n    <string name=\"dms_inbox_shared_profile\">%s udostępnił profil: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s udostępnił lokalizację: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s udostępnił story @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s udostępnił story @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s wysłał wiadomość głosową</string>\n    <string name=\"dms_inbox_shared_clip\">%s udostępnił klip @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s udostępnił film IGTV @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Odpowiedziałeś na relację: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s odpowiedział na Twoją relację: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Zareagowałeś na relację: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s zareagował na Twoją relację: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Wspomniałeś @%s w swojej relacji</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s wspomniał o tobie w swojej relacji</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Nieznany typ mediów</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media wygasły!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Dostarczono</string>\n    <string name=\"dms_inbox_raven_media_sent\">Wysłano</string>\n    <string name=\"dms_inbox_raven_media_opened\">Otwarto</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Odtworzono ponownie</string>\n    <string name=\"dms_inbox_raven_media_sending\">Wysyłanie…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Zablokowano</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Proponowane</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Dokonano zrzutu ekranu</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Nie dostarczono</string>\n    <string name=\"dms_thread_message_hint\">Komunikat…</string>\n    <string name=\"dms_thread_audio_hint\">Naciśnij i przytrzymaj, aby nagrać dźwięk</string>\n    <string name=\"dms_thread_updating\">Aktualizowanie…</string>\n    <string name=\"dms_action_leave\">Opuść czat</string>\n    <string name=\"dms_action_leave_question\">Opuścić ten czat?</string>\n    <string name=\"dms_action_kick\">Wyrzuć</string>\n    <string name=\"dms_left_users\">Pozostali użytkownicy</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Nieprawidłowy użytkownik</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram nie pozwala na przesyłanie filmów dłuższych niż 60 sekund w wiadomości prywatnej.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram nie pozwala na przesyłanie dźwięku dłuższego niż 60 sekund.</string>\n    <string name=\"direct_download_loading\">Pobieranie post(ów)</string>\n    <string name=\"downloader_complete\">Pobieranie zakończone</string>\n    <string name=\"downloader_preparing\">Przygotowywanie do pobrania…</string>\n    <string name=\"downloader_downloading_post\">Pobieranie wpisu…</string>\n    <string name=\"downloader_downloading_media\">Pobieranie multimediów</string>\n    <string name=\"downloader_unknown_error\">Wystąpił nieznany błąd!!!</string>\n    <string name=\"downloader_error_creating_folder\">Błąd podczas tworzenia folderu!</string>\n    <string name=\"downloader_error_download_file\">Błąd podczas pobierania pliku</string>\n    <string name=\"comment_viewer_translate_comment\">Tłumacz komentarz</string>\n    <string name=\"comment_viewer_delete_comment\">Usuń komentarz</string>\n    <string name=\"followers_type_followers\">Obserwujący</string>\n    <string name=\"followers_type_following\">Obserwuje</string>\n    <string name=\"followers_compare\">Porównywanie obserwujących &amp; obserwowanych</string>\n    <string name=\"followers_both_following\">Wzajemna obserwacja z obu stron</string>\n    <string name=\"followers_not_following\">nie obserwuje %s</string>\n    <string name=\"followers_not_follower\">%s not following</string>\n    <string name=\"login_error_loading_cookies\">Błąd ładowania ciasteczek</string>\n    <string name=\"comment_hint\">Napisz nowy komentarz…</string>\n    <string name=\"liked_notif\">Polubiono twój post</string>\n    <string name=\"comment_notif\">Skomentowano Twój post:</string>\n    <string name=\"follow_notif\">Zaczął Cię obserwować</string>\n    <string name=\"tagged_notif\">Oznaczono cię w poście</string>\n    <string name=\"request_notif\">Poproszono o zgodę na obserwowanie</string>\n    <string name=\"request_approve\">Potwierdź</string>\n    <string name=\"request_reject\">Odrzuć</string>\n    <string name=\"share_public_post\">Udostępnij ten publiczny post do…</string>\n    <string name=\"share_private_post\">To jest prywatny post! Udostępnij wszystkim, którzy mogą go zobaczyć.</string>\n    <string name=\"discover_empty\">Ta kategoria jest jakoś pusta…</string>\n    <string name=\"update_available\">Aktualizacja jest dostępna! (%s)</string>\n    <string name=\"updated\">Dziękujemy za aktualizację Barinsta!</string>\n    <string name=\"crash_title\">Aplikacja uległa awarii</string>\n    <string name=\"crash_descr\">Ups.. aplikacja uległa awarii, ale nie martw się, możesz wysłać raport o błędzie do dewelopera, aby pomóc mu rozwiązać ten problem (:</string>\n    <string name=\"action_notif\">Aktywność</string>\n    <string name=\"action_archive\">Archiwum relacji</string>\n    <string name=\"action_ayml\">Proponowani użytkownicy</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">Masz %d nowe powiadomienie</item>\n        <item quantity=\"few\">You have %d notifications</item>\n        <item quantity=\"many\">You have %d notifications</item>\n        <item quantity=\"other\">Masz %d nowych powiadomień</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d obserwujących</string>\n    <string name=\"activity_count_comments\">%d komentarzy</string>\n    <string name=\"activity_count_commentlikes\">%d polubionych komentarzy</string>\n    <string name=\"activity_count_usertags\">%d tagów użytkowników</string>\n    <string name=\"activity_count_likes\">%d polubień</string>\n    <string name=\"activity_count_poy\">%d zdjęć z Tobą</string>\n    <string name=\"activity_count_requests\">%d próśb o o obserwowanie</string>\n    <string name=\"activity_notloggedin\">Wylogowałeś się przed kliknięciem tego powiadomienia?!</string>\n    <string name=\"feed\">Aktualności</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Więcej</string>\n    <string name=\"title_dm\">PW</string>\n    <string name=\"number_selected\">%d wybranych</string>\n    <string name=\"logout_success\">Wylogowano pomyślnie!</string>\n    <string name=\"dm_thread_info\">Informacje</string>\n    <string name=\"mark_as_seen\">Oznacz jako przeczytane</string>\n    <string name=\"version\">Wersja</string>\n    <string name=\"pref_start_screen\">Ekran startowy</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Pokaż klawiaturę przy wyszukiwaniu</string>\n    <string name=\"pref_category_general\">Ogólne</string>\n    <string name=\"pref_category_theme\">Motyw</string>\n    <string name=\"pref_category_downloads\">Pobieranie</string>\n    <string name=\"pref_category_locale\">Język</string>\n    <string name=\"account\">Konto</string>\n    <string name=\"account_hint\">Bieżące logowanie nie działa? Po prostu dodaj konto ponownie.</string>\n    <string name=\"add_account\">Dodaj konto</string>\n    <string name=\"about_category_license\">Licencja (tylko w języku angielskim)</string>\n    <string name=\"about_documentation\">Odwiedź naszą stronę internetową</string>\n    <string name=\"about_documentation_summary\">Uzyskaj wsparcie, dyskutuj, poznaj innych i baw się dobrze!</string>\n    <string name=\"about_repository\">Zobacz nasz kod źródłowy na GitHub</string>\n    <string name=\"about_repository_summary\">Audytuj, oznaczaj gwiazdkami, zgłaszaj błędy, współtwórz i baw się (ponownie)!</string>\n    <string name=\"about_feedback\">Wyślij opinię za pomocą e-mail</string>\n    <string name=\"about_category_3pt\">Atrybuty zewnętrzne</string>\n    <string name=\"reminder\">Przypomnienie</string>\n    <string name=\"reminder_summary\">Korzystaj z tej aplikacji odpowiedzialnie. Pobrane obrazy powinny być używane tylko do celów dozwolonych przez obowiązujące przepisy.</string>\n    <string name=\"light_white_theme\">Biały</string>\n    <string name=\"dark_black_theme\">Czarny</string>\n    <string name=\"light_theme_settings\">Jasny motyw</string>\n    <string name=\"dark_theme_settings\">Ciemny motyw</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Dodano do ulubionych!</string>\n    <string name=\"add_to_favorites\">Dodaj do ulubionych</string>\n    <string name=\"accounts\">Konta</string>\n    <string name=\"hashtags\">Hashtagi</string>\n    <string name=\"locations\">Lokalizacje</string>\n    <string name=\"unknown\">Nieznany</string>\n    <string name=\"removed_from_favs\">Usunięto z ulubionych!</string>\n    <string name=\"backup_and_restore\">Kopia zapasowa &amp; Przywracanie</string>\n    <string name=\"auto_backup\">Automatyczna Kopia Zapasowa</string>\n    <string name=\"auto_backup_summary\">Począwszy od Androida 6, funkcja automatycznego tworzenia kopii zapasowej będzie przesyłać wszystkie ustawienia aplikacji, dane logowania konta, i ulubionych na Dysk Google. Mogą one zostać przywrócone przez ponowną instalację aplikacji po jej odinstalowaniu.</string>\n    <string name=\"auto_backup_warning\">To ustawienie nie będzie działać, jeśli usługi Google Play nie są zainstalowe, lub jeśli automatyczne kopie zapasowe są wyłączone w ustawieniach urządzenia. Wyłączenie tutaj nie usuwa istniejących kopii.</string>\n    <string name=\"auto_backup_setting\">Włącz automatyczne tworzenie kopii zapasowej</string>\n    <string name=\"manual_backup\">Ręczna Kopia Zapasowa</string>\n    <string name=\"backup_summary\">Kopia zapasowa ustawień aplikacji Barinsta, danych logowania do konta i/lub ulubionych do zwykłego tekstu lub zaszyfrowanego pliku kopii zapasowej w celu późniejszego przywrócenia.</string>\n    <string name=\"backup_warning\">Jeśli tworzysz kopię zapasową danych logowania do konta, potraktuj plik jako poufny i przechowuj go w bezpiecznym miejscu!</string>\n    <string name=\"create_backup\">Utwórz nowy plik kopii zapasowej</string>\n    <string name=\"restore_backup\">Przywróć z istniejącego pliku kopii zapasowej</string>\n    <string name=\"file_chosen_label\">Plik:</string>\n    <string name=\"enter_password\">Wprowadź hasło</string>\n    <string name=\"select_backup_file\">Wybierz plik kopii zapasowej (.zaai/.backup)</string>\n    <string name=\"apply\">Zastosuj</string>\n    <string name=\"save\">Zapisz</string>\n    <string name=\"caption\">Nagłówek</string>\n    <string name=\"edit_caption\">Edytuj opis</string>\n    <string name=\"translate_caption\">Tłumacz opis</string>\n    <string name=\"player_timeline_desc\">Oś czasu odtwarzacza wideo</string>\n    <string name=\"liking\">Przesyłanie polubienia…</string>\n    <string name=\"like_unsuccessful\">Nie udało się polubić</string>\n    <string name=\"unlike_unsuccessful\">Nie udało się cofnąć polubienia</string>\n    <string name=\"unliking\">Cofanie polubienia…</string>\n    <string name=\"controls\">Ustawienia</string>\n    <string name=\"saving\">Zapisywanie…</string>\n    <string name=\"removing\">Usuwanie…</string>\n    <string name=\"save_unsuccessful\">Zapisywanie nie powiodło się</string>\n    <string name=\"save_remove_unsuccessful\">Usuwanie nie powiodło się</string>\n    <string name=\"downloading\">Pobieranie…</string>\n    <string name=\"downloader_downloading_child\">Pobieranie elementów %1$d z %2$d</string>\n    <string name=\"delete\">Usuń</string>\n    <string name=\"comment\">Komentarz</string>\n    <string name=\"layout\">Układ</string>\n    <string name=\"feed_stories\">Relacje</string>\n    <string name=\"opening_post\">Otwieranie wpisu…</string>\n    <string name=\"share\">Udostępnij</string>\n    <string name=\"layout_style\">Styl układu</string>\n    <string name=\"column_count\">Liczba kolumn</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Pokaż nazwy</string>\n    <string name=\"show_avatars\">Pokaż awatary</string>\n    <string name=\"avatar_size\">Wymiary awatara</string>\n    <string name=\"corners\">Narożniki</string>\n    <string name=\"show_grid_gap\">Pokaż widok siatki</string>\n    <string name=\"post_not_found\">Nie znaleziono posta!</string>\n    <string name=\"no_external_app_url\">Nie znaleziono aplikacji dla otwierania adresów URL</string>\n    <string name=\"gallery\">Galeria</string>\n    <string name=\"camera\">Aparat</string>\n    <string name=\"all_photos\">Wszystkie zdjęcia</string>\n    <string name=\"all_media\">Wszystkie multimedia</string>\n    <string name=\"all_videos\">Wszystkie wideo</string>\n    <string name=\"brightness\">Jasność</string>\n    <string name=\"contrast\">Kontrast</string>\n    <string name=\"vibrance\">Jaskrawość</string>\n    <string name=\"saturation\">Nasycenie</string>\n    <string name=\"sharpen\">Ostrość</string>\n    <string name=\"exposure\">Ekspozycja</string>\n    <string name=\"center\">Wyśrodkuj</string>\n    <string name=\"color\">Kolor</string>\n    <string name=\"start\">Rozpocznij</string>\n    <string name=\"end\">Zakończ</string>\n    <string name=\"bilateral_blur\">Rozmycie dwustronne</string>\n    <string name=\"vignette\">Winieta</string>\n    <string name=\"box_blur\">Rozmycie pola</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Klauzula</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Reset</string>\n    <string name=\"crop\">Wytnij</string>\n    <string name=\"normal\">Normalny</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d wyświetlenie</item>\n        <item quantity=\"few\">%d wyświetleń</item>\n        <item quantity=\"many\">%d wyświetleń</item>\n        <item quantity=\"other\">%d wyświetlenia</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s relacja</item>\n        <item quantity=\"few\">%s relacji</item>\n        <item quantity=\"many\">%s relacji</item>\n        <item quantity=\"other\">%s relacji</item>\n    </plurals>\n    <string name=\"details\">Szczegóły</string>\n    <string name=\"title\">Tytuł</string>\n    <string name=\"members\">Członkowie</string>\n    <string name=\"admin\">Administrator</string>\n    <string name=\"inviter\">Zapraszający</string>\n    <string name=\"mute_messages\">Wyciszone wiadomości</string>\n    <string name=\"mute_mentions\">Wyciszone wzmianki</string>\n    <string name=\"add_members\">Dodaj członków</string>\n    <string name=\"search\">Szukaj</string>\n    <string name=\"done\">Gotowe</string>\n    <string name=\"dms_action_make_admin\">Nadaj uprawnienia administratora</string>\n    <string name=\"dms_action_remove_admin\">Usuń uprawnienia administratora</string>\n    <string name=\"edit_unsuccessful\">Edycja nie powiodła się</string>\n    <string name=\"message\">Wiadomość</string>\n    <string name=\"tap_to_remove\">Dotknij, aby usunąć</string>\n    <string name=\"forward\">Przekaż dalej</string>\n    <string name=\"forward_outgoing\">Przesłano wiadomość</string>\n    <string name=\"forward_incoming\">Przekazano wiadomość</string>\n    <string name=\"add\">Dodaj</string>\n    <string name=\"send\">Wyślij</string>\n    <string name=\"replying_to_yourself\">Odpowiedz sobie</string>\n    <string name=\"replying_to_user\">Odpowiadanie %s</string>\n    <string name=\"replied_to_yourself\">Odpowiedziałeś sobie</string>\n    <string name=\"replied_you\">Odpowiedziałeś</string>\n    <string name=\"replied_you_group\">Odpowiedziałeś %s</string>\n    <string name=\"replied_group\">Odpowiadanie na %s</string>\n    <string name=\"replied_to_you\">Odpowiedział Tobie</string>\n    <string name=\"replied_to_themself\">Odpowiedział sobie</string>\n    <string name=\"reacted_story_outgoing\">Zareagowałeś na relację</string>\n    <string name=\"reacted_story_incoming\">Zareagował na Twoją relację</string>\n    <string name=\"mentioned_story_outgoing\">Wspomniałeś o nich w swojej relacji</string>\n    <string name=\"mentioned_story_incoming\">Wspomnieli o Tobie w swojej relacji</string>\n    <string name=\"replied_story_outgoing\">Odpowiedziałeś na ich relację</string>\n    <string name=\"replied_story_incoming\">Odpowiedział na Twoją relację</string>\n    <string name=\"raven_image_expired\">Zdjęcie wygasło</string>\n    <string name=\"raven_image_info\">Obraz wygaśnie po wyświetleniu</string>\n    <string name=\"raven_video_expired\">Film wygasł</string>\n    <string name=\"raven_video_info\">Wideo wygaśnie po wyświetleniu</string>\n    <string name=\"raven_msg_expired\">Wiadomość wygasła</string>\n    <string name=\"raven_msg_info\">Wiadomość wygaśnie po wyświetleniu</string>\n    <string name=\"story_share\">Relacja @%s</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Zdjęcie</string>\n    <string name=\"video\">Film</string>\n    <string name=\"voice_message\">Wiadomość głosowa</string>\n    <string name=\"post\">Publikuj</string>\n    <string name=\"approval_required_for_new_members\">Zatwierdź prośbę o dołączenie</string>\n    <string name=\"requests\">Zapytania</string>\n    <string name=\"admins_only\">Tylko administratorzy</string>\n    <string name=\"added_by\">Dodany przez %s</string>\n    <string name=\"admin_approval_required\">Wymagane zatwierdzenie przez administratora</string>\n    <string name=\"admin_approval_required_description\">Aby dodać nowych członków do grupy wymagane będzie zatwierdzenie przez administratora</string>\n    <string name=\"dms_action_end\">Zakończ chat</string>\n    <string name=\"dms_action_end_question\">Zakończyć czat?</string>\n    <string name=\"dms_action_end_description\">Wszyscy członkowie zostaną usunięci z grupy. Będą mogli przeglądać historię czatu.</string>\n    <string name=\"pending_requests\">Zgłoszenia oczekujące</string>\n    <string name=\"accept_request_from_user\">Zaakceptować prośbę od %1s (%2s)?</string>\n    <string name=\"decline\">Odrzuć</string>\n    <string name=\"accept\">Akceptuj</string>\n    <string name=\"you\">Ty</string>\n    <string name=\"no_pending_requests\">Brak oczekujących żądań</string>\n    <string name=\"checking_for_new_messages\">Sprawdzanie nowych wiadomości</string>\n    <string name=\"pref_category_stories\">Relacje</string>\n    <string name=\"pref_category_dm\">PW</string>\n    <string name=\"pref_category_notifications\">Powiadomienia</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">Włącz powiadomienia PW</string>\n    <string name=\"enable_dm_auto_refesh\">Automatyczne odświeżanie wiadomości</string>\n    <string name=\"auto_refresh_every\">Automatycznie odświeżaj co</string>\n    <string name=\"secs\">s.</string>\n    <string name=\"mins\">min.</string>\n    <string name=\"search_giphy\">Szukaj GIPHY</string>\n    <string name=\"generic_null_response\">Odpowiedź jest pusta!</string>\n    <string name=\"generic_not_ok_response\">Status odpowiedzi nie jest OK!</string>\n    <string name=\"generic_failed_request\">Żądanie nie powiodło się!</string>\n    <string name=\"hint_keyword\">Słowo kluczowe</string>\n    <string name=\"toggle_keyword_filter\">Włącz filtr słów kluczowych</string>\n    <string name=\"edit_keyword_filter\">Edytuj filtry słów kluczowych</string>\n    <string name=\"added_keywords\">Dodano słowo kluczowe: %s do listy filtrów</string>\n    <string name=\"removed_keywords\">Usunięto słowo kluczowe: %s z listy filtrów</string>\n    <string name=\"marked_as_seen\">Oznacz jako przeczytane</string>\n    <string name=\"delete_unsuccessful\">Usuwanie nie powiodło się</string>\n    <string name=\"throttle_error\">Tłumacz został ograniczony z powodu zbyt wielu żądań API. Poczekaj chwilę i spróbuj ponownie.</string>\n    <string name=\"error\">Błąd</string>\n    <string name=\"account_logged_out\">To konto zostało wylogowane.</string>\n    <string name=\"login_required\">Wymagane logowanie!</string>\n    <string name=\"inactive_user\">Użytkownik jest nieaktywny!</string>\n    <string name=\"crash_report_subject\">Raport awarii Barinsta</string>\n    <string name=\"crash_report_title\">Wybierz aplikację e-mail do wysyłania dzienników awarii</string>\n    <string name=\"not_found\">Nie znaleziono!</string>\n    <string name=\"rate_limit\">Twój adres IP został ograniczony przez Instagrama. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Dowiedz się więcej.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Pomiń tę aktualizację</string>\n    <string name=\"on_latest_version\">Posiadasz aktualną wersję</string>\n    <string name=\"tab_order\">Kolejność na ekranie</string>\n    <string name=\"other_tabs\">Inne karty</string>\n    <string name=\"tab_order_start_next_launch\">Kolejność zakładek zostanie odzwierciedlona przy następnym uruchomieniu</string>\n    <string name=\"dm_remove_warning\">Po zapisaniu wszystkie funkcje związane z PW zostaną wyłączone przy następnym uruchomieniu</string>\n    <string name=\"copy_caption\">Kopiuj podpis</string>\n    <string name=\"copy_reply\">Kopiuj odpowiedź</string>\n    <string name=\"restore\">Przywróć</string>\n    <string name=\"backup\">Kopia zapasowa</string>\n    <string name=\"dir_select_default_message\">Wybierz folder, w którym Barinsta może przechowywać pobrane pliki i pliki tymczasowe.\\n\\nMożesz to później zmienić w Więcej &gt; Ustawienia &gt; Pobieranie.</string>\n    <string name=\"dir_select_reselect_message\">Android zmienił sposób, w jaki aplikacje mogą uzyskać dostęp do plików i katalogów w pamięci urządzenia. Obecnie Barinsta nie ma uprawnień do dostępu do następującego folderu:</string>\n    <string name=\"dir_select_permission_revoked_message\">Uprawnienia dla poprzednio wybranego folderu zostały cofnięte przez system:</string>\n    <string name=\"dir_select_folder_not_exist\">Poprzednio wybrany folder nie istnieje:</string>\n    <string name=\"dir_select_message2\">Wybierz ponownie folder lub wybierz now klikając przycisk poniżej.</string>\n    <string name=\"select_a_folder\">Nie wybrano folderu!</string>\n    <string name=\"dir_select_no_download_folder\">Wybierz katalog z pamięci, a nie kategorię na pasku bocznym.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Sukces! Proszę czekać. Uruchamianie aplikacji…</string>\n    <string name=\"barinsta_folder\">Folder Barinsta</string>\n    <string name=\"top\">Najlepsze</string>\n    <string name=\"recent\">Najnowsze</string>\n    <string name=\"clear\">Wyczyść</string>\n    <string name=\"no_external_map_app\">Nie znaleziono aplikacji do obsługi mapy!</string>\n    <string name=\"click_to_show_full\">Kliknij, aby wyświetlić pełną liczbę polubień</string>\n    <string name=\"no_profile_pic_found\">Nie znaleziono zdjęcia profilowego!</string>\n    <string name=\"swipe_up_confirmation\">Czy na pewno chcesz otworzyć ten link?</string>\n    <string name=\"sending\">Wysyłanie…</string>\n    <string name=\"share_via_dm\">Udostępnij przez PW</string>\n    <string name=\"share_link\">Udostępnij link…</string>\n    <string name=\"slide_to_cancel\">Przesuń, by anulować</string>\n    <string name=\"disable_screen_transitions\">Wyłącz przejścia ekranu</string>\n    <string name=\"invalid_format\">Nieprawidłowy format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta nie może uruchomić menedżera plików. Upewnij się, że jest on zainstalowana i włączona na twoim urządzeniu.</string>\n    <string name=\"story_stickers\">Naklejki</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Padrão do Sistema</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automático (Definido pelo sistema)</item>\n        <item>Automático (Definido pela Bateria)</item>\n        <item>Escuro</item>\n        <item>Claro</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Padrão do Instagram (Não lido, em seguida, lido)</item>\n        <item>Do mais novo ao mais antigo</item>\n        <item>Do mais antigo ao mais novo</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Nenhum</item>\n        <item>\\@</item>\n        <item>as</item>\n        <item>em</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>segs</item>\n        <item>mins</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Sobre</string>\n    <string name=\"action_dms\">Mensagens Diretas</string>\n    <string name=\"action_settings\">Configurações</string>\n    <string name=\"action_download\">Baixar</string>\n    <string name=\"action_search\">Buscar nome de usuário…</string>\n    <string name=\"action_compare\">Comparar</string>\n    <string name=\"clipboard_error\">Erro ao copiar texto</string>\n    <string name=\"clipboard_copied\">Copiado para a área de transferência!</string>\n    <string name=\"report\">Reportar</string>\n    <string name=\"set_password\">Proteger arquivo com senha</string>\n    <string name=\"password_no_max\">Senha</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Sim</string>\n    <string name=\"cancel\">Cancelar</string>\n    <string name=\"no\">Não</string>\n    <string name=\"confirm\">Confirmar</string>\n    <string name=\"title_favorites\">Favoritos</string>\n    <string name=\"title_discover\">Descobrir</string>\n    <string name=\"title_comments\">Comentários</string>\n    <string name=\"title_replies\">Respostas</string>\n    <string name=\"title_notifications\">Atividade</string>\n    <string name=\"update_check\">Verificar se há atualizações ao iniciar</string>\n    <string name=\"flag_secure\">Bloquear capturas de tela e a pré-visualização do aplicativo</string>\n    <string name=\"download_user_folder\">Baixar publicações para pastas com o nome de usuário</string>\n    <string name=\"download_prepend_username\">Adicionar Nome de usuário ao Nome do arquivo</string>\n    <string name=\"mark_as_seen_setting\">Marcar stories como vistos após a visualização</string>\n    <string name=\"mark_as_seen_setting_summary\">O autor do story saberá que você viu</string>\n    <string name=\"hide_muted_reels_setting\">Esconder stories silenciados do feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Marcar DM como vista após a visualização</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Outros membros saberão que você viu</string>\n    <string name=\"autoplay_stories_setting\">Reproduzir vídeos dos stories automaticamente</string>\n    <string name=\"story_list_setting\">Mostrar lista de stories por padrão</string>\n    <string name=\"story_list_setting_summary\">Para ver stories</string>\n    <string name=\"activity_setting\">Ativar notificações de atividade</string>\n    <string name=\"story_sort_setting\">Ordenação dos stories</string>\n    <string name=\"error_loading_profile\">Erro ao carregar perfil! O nome de usuário é válido? Se sim, você pode ter sido limitado pelo Instagram.</string>\n    <string name=\"error_loading_hashtag\">Erro ao carregar hashtag! O nome é válido?</string>\n    <string name=\"error_loading_location\">Erro ao carregar localização! A URL é válida?</string>\n    <string name=\"error_creating_folders\">Erro ao criar pasta(s) de download.</string>\n    <string name=\"select_folder\">Selecionar pasta</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Idioma</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPublicação</item>\n        <item quantity=\"other\">%s\\nPublicações</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Publicação</item>\n        <item quantity=\"other\">%s Publicações</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSeguidor</item>\n        <item quantity=\"other\">%s\\nSeguidores</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nSeguindo</string>\n    <string name=\"post_viewer_autoplay_video\">Reprodução automática de vídeos</string>\n    <string name=\"post_viewer_background_play\">Continuar vídeos em segundo plano</string>\n    <string name=\"post_viewer_background_play_summary\">Não pausar vídeos quando o aplicativo está fora de foco</string>\n    <string name=\"post_viewer_muted_autoplay\">Sempre silenciar vídeos</string>\n    <string name=\"post_viewer_show_captions\">Sempre mostrar as legendas das publicações</string>\n    <string name=\"post_viewer_download_dialog_title\">Selecionar o que baixar</string>\n    <string name=\"post_viewer_download_current\">Atual</string>\n    <string name=\"post_viewer_download_album\">Álbum Inteiro</string>\n    <string name=\"show_stories\">Mostrar stories</string>\n    <string name=\"no_more_stories\">Sem mais stories!</string>\n    <string name=\"view_post\">Ver Publicação</string>\n    <string name=\"story_poll\">Enquete</string>\n    <string name=\"answered_story\">Resposta enviada!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d resposta em uma média de %s</item>\n        <item quantity=\"other\">%d respostas em uma média de %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Sua resposta: %s</string>\n    <string name=\"reply_story\">Responder o story</string>\n    <string name=\"reply_hint\">Responder…</string>\n    <string name=\"story_quiz\">Quiz</string>\n    <string name=\"story_slider\">Deslizante</string>\n    <string name=\"story_quizzed\">Você já respondeu!</string>\n    <string name=\"story_mentions\">Menções</string>\n    <string name=\"story_question\">Pergunta</string>\n    <string name=\"priv_acc\">Esta conta é privada</string>\n    <string name=\"priv_acc_confirm\">Você não será capaz de acessar as publicações após deixar de seguir! Tem certeza?</string>\n    <string name=\"are_you_sure\">Tem certeza?</string>\n    <string name=\"no_acc\">Você pode fazer login em Mais -&gt; Conta no canto inferior direito ou você pode ver as contas públicas sem fazer login!</string>\n    <string name=\"empty_acc\">Esta conta não tem publicações</string>\n    <string name=\"empty_list\">Não há publicações!</string>\n    <string name=\"login\">Logar</string>\n    <string name=\"logout\">Deslogar</string>\n    <string name=\"logout_summary\">Explorar o Instagram anonimamente</string>\n    <string name=\"remove_all_acc\">Remover todas as contas</string>\n    <string name=\"remove_all_acc_warning\">Isto removerá todas as contas adicionadas do aplicativo!\\nPara remover apenas uma conta, aperte e segure na conta na área de troca de conta.\\nVocê quer continuar?</string>\n    <string name=\"time_settings\">Formato de data</string>\n    <string name=\"saved_create_collection\">Criar nova coleção</string>\n    <string name=\"edit_collection\">Editar o nome da coleção</string>\n    <string name=\"delete_collection\">Excluir coleção</string>\n    <string name=\"delete_collection_note\">Todas as publicações contidas na coleção excluída permanecerão em outras coleções.</string>\n    <string name=\"add_to_collection\">Adicionar à coleção…</string>\n    <string name=\"remove_from_collection\">Remover da coleção</string>\n    <string name=\"liked\">Curtiu</string>\n    <string name=\"saved\">Salvo</string>\n    <string name=\"tagged\">Marcado</string>\n    <string name=\"dm_person\">Mensagem</string>\n    <string name=\"follow\">Seguir</string>\n    <string name=\"unfollow\">Deixar de seguir</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorito</string>\n    <string name=\"block\">Bloquear</string>\n    <string name=\"unblock\">Desbloquear</string>\n    <string name=\"restrict\">Restringir</string>\n    <string name=\"unrestrict\">Deixar de restringir</string>\n    <string name=\"mute_stories\">Silenciar stories</string>\n    <string name=\"mute_posts\">Silenciar publicações</string>\n    <string name=\"unmute_stories\">Reativar stories</string>\n    <string name=\"unmute_posts\">Reativar publicações</string>\n    <string name=\"remove_follower\">Remover seguidor</string>\n    <string name=\"bio_copy\">Copiar bio</string>\n    <string name=\"bio_translate\">Traduzir bio</string>\n    <string name=\"status_mutual\">Mútuo</string>\n    <string name=\"status_following\">Seguindo</string>\n    <string name=\"status_follower\">Seguidor</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Contas</string>\n    <string name=\"dialog_export_settings\">Configurações</string>\n    <string name=\"dialog_export_favorites\">Favoritos</string>\n    <string name=\"dialog_import_success\">Importado com sucesso!</string>\n    <string name=\"dialog_import_failed\">Falha ao importar!</string>\n    <string name=\"dialog_export_success\">Exportado com sucesso!</string>\n    <string name=\"dialog_export_failed\">Falha ao exportar!</string>\n    <string name=\"refresh\">Atualizar</string>\n    <string name=\"get_cookies\">Obter cookies</string>\n    <string name=\"time_settings_title_custom\">Usar formato personalizado</string>\n    <string name=\"time_settings_title_separator\">Separador</string>\n    <string name=\"time_settings_title_time_format\">Formato de hora</string>\n    <string name=\"time_settings_title_date_format\">Formato de data</string>\n    <string name=\"time_settings_title_preview\">Pré-visualizar</string>\n    <string name=\"time_settings_swap_time\">Trocar posições de Data e Hora</string>\n    <string name=\"quick_access_cannot_delete_curr\">Não é possível excluir a conta atualmente em uso</string>\n    <string name=\"quick_access_confirm_delete\">Tem certeza que deseja excluir %s\\'?</string>\n    <string name=\"open_profile\">Abrir perfil</string>\n    <string name=\"view_story\">Ver story</string>\n    <string name=\"view_pfp\">Ver foto de perfil</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Tipo de mensagem não suportado</string>\n    <string name=\"dms_inbox_unsend\">Cancelar envio</string>\n    <string name=\"dms_inbox_giphy\">Ver no GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s compartilhou uma publicação de @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s compartilhou uma imagem</string>\n    <string name=\"dms_inbox_shared_video\">%s compartilhou um vídeo</string>\n    <string name=\"dms_inbox_shared_message\">%s enviou uma mensagem</string>\n    <string name=\"dms_inbox_shared_gif\">%s compartilhou um gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s compartilhou uma figurinha</string>\n    <string name=\"dms_inbox_shared_profile\">%s compartilhou um perfil: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s compartilhou uma localização: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s compartilhou um story em destaque de @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s compartilhou um story de @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s enviou uma mensagem de voz</string>\n    <string name=\"dms_inbox_shared_clip\">%s compartilhou um clipe de @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s compartilhou um vídeo IGTV de @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Você respondeu ao seu story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s respondeu ao seu story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Você reagiu ao seu story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reagiu ao seu story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Você mencionou @%s em seu story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mencionou você em seu story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Tipo de mídia desconhecido</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Mídia expirada!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Entregue</string>\n    <string name=\"dms_inbox_raven_media_sent\">Enviado</string>\n    <string name=\"dms_inbox_raven_media_opened\">Aberto</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Repetido</string>\n    <string name=\"dms_inbox_raven_media_sending\">Enviando…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Bloqueado</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Sugerido</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Capturado</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Não pode ser entregue</string>\n    <string name=\"dms_thread_message_hint\">Mensagem…</string>\n    <string name=\"dms_thread_audio_hint\">Aperte e segure para gravar um áudio</string>\n    <string name=\"dms_thread_updating\">Atualizando…</string>\n    <string name=\"dms_action_leave\">Sair da conversa</string>\n    <string name=\"dms_action_leave_question\">Sair deste chat?</string>\n    <string name=\"dms_action_kick\">Expulsar</string>\n    <string name=\"dms_left_users\">Usuários que saíram</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Usuário inválido</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">O Instagram não permite o upload de vídeos com mais de 60 segundos para DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">O Instagram não permite o upload de áudios com mais de 60 segundos.</string>\n    <string name=\"direct_download_loading\">Buscando publicação(ões)</string>\n    <string name=\"downloader_complete\">Download concluído</string>\n    <string name=\"downloader_preparing\">Preparando o download…</string>\n    <string name=\"downloader_downloading_post\">Baixando publicação…</string>\n    <string name=\"downloader_downloading_media\">Baixando mídia</string>\n    <string name=\"downloader_unknown_error\">Ocorreu um erro desconhecido!!!</string>\n    <string name=\"downloader_error_creating_folder\">Erro ao criar pasta!</string>\n    <string name=\"downloader_error_download_file\">Erro ao baixar arquivo</string>\n    <string name=\"comment_viewer_translate_comment\">Traduzir comentário</string>\n    <string name=\"comment_viewer_delete_comment\">Excluir comentário</string>\n    <string name=\"followers_type_followers\">Seguidores</string>\n    <string name=\"followers_type_following\">Seguindo</string>\n    <string name=\"followers_compare\">Comparar seguidores e seguindo</string>\n    <string name=\"followers_both_following\">Ambos se seguem</string>\n    <string name=\"followers_not_following\">não segue %s</string>\n    <string name=\"followers_not_follower\">%s não segue</string>\n    <string name=\"login_error_loading_cookies\">Erro ao carregar cookies</string>\n    <string name=\"comment_hint\">Escrever um novo comentário…</string>\n    <string name=\"liked_notif\">Curtiu sua publicação</string>\n    <string name=\"comment_notif\">Comentou em sua publicação:</string>\n    <string name=\"follow_notif\">Começou a seguir você</string>\n    <string name=\"tagged_notif\">Marcou você em uma publicação</string>\n    <string name=\"request_notif\">Pediu para seguir você</string>\n    <string name=\"request_approve\">Aprovar pedido</string>\n    <string name=\"request_reject\">Rejeitar pedido</string>\n    <string name=\"share_public_post\">Compartilhar esta publicação pública com…</string>\n    <string name=\"share_private_post\">Este é um posto privado! Compartilhe com aqueles que podem vê-lo.</string>\n    <string name=\"discover_empty\">Esta categoria está vazia…</string>\n    <string name=\"update_available\">Uma atualização está disponível! (%s)</string>\n    <string name=\"updated\">Obrigado por atualizar o Barinsta!</string>\n    <string name=\"crash_title\">O aplicativo travou</string>\n    <string name=\"crash_descr\">Ops... o aplicativo travou, mas não se preocupe, você pode enviar um relatório de erro para o desenvolvedor para ajudá-lo a corrigir o problema. (:</string>\n    <string name=\"action_notif\">Atividade</string>\n    <string name=\"action_archive\">Arquivo de stories</string>\n    <string name=\"action_ayml\">Usuários sugeridos</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">Você tem %d notificação</item>\n        <item quantity=\"other\">Você tem %d notificações</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d seguidores</string>\n    <string name=\"activity_count_comments\">%d comentários</string>\n    <string name=\"activity_count_commentlikes\">%d comentários curtidos</string>\n    <string name=\"activity_count_usertags\">%d marcações</string>\n    <string name=\"activity_count_likes\">%d curtidas</string>\n    <string name=\"activity_count_poy\">%d fotos de você</string>\n    <string name=\"activity_count_requests\">%d pedidos para te seguir</string>\n    <string name=\"activity_notloggedin\">Você deslogou antes de clicar nessa notificação?!</string>\n    <string name=\"feed\">Feed</string>\n    <string name=\"profile\">Perfil</string>\n    <string name=\"more\">Mais</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d selecionado</string>\n    <string name=\"logout_success\">Deslogado com sucesso!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Marcar como lido</string>\n    <string name=\"version\">Versão</string>\n    <string name=\"pref_start_screen\">Tela inicial</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Mostrar teclado na busca</string>\n    <string name=\"pref_category_general\">Geral</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Downloads</string>\n    <string name=\"pref_category_locale\">Localização</string>\n    <string name=\"account\">Conta</string>\n    <string name=\"account_hint\">O login atual não está funcionando? Basta adicionar a conta novamente.</string>\n    <string name=\"add_account\">Adicionar conta</string>\n    <string name=\"about_category_license\">Licença (Apenas em inglês)</string>\n    <string name=\"about_documentation\">Visite nosso site</string>\n    <string name=\"about_documentation_summary\">Obtenha suporte, discuta, conheça outras pessoas e divirta-se!</string>\n    <string name=\"about_repository\">Veja nosso código-fonte no GitHub</string>\n    <string name=\"about_repository_summary\">Verifique, favorite, reporte bugs, contribua e divirta-se (de novo)!</string>\n    <string name=\"about_feedback\">Enviar feedback por email</string>\n    <string name=\"about_category_3pt\">Atribuições de terceiros</string>\n    <string name=\"reminder\">Lembrete</string>\n    <string name=\"reminder_summary\">Por favor, use este aplicativo de forma responsável. As imagens baixadas só devem ser usadas para fins permitidos pelas leis aplicáveis.</string>\n    <string name=\"light_white_theme\">Branco</string>\n    <string name=\"dark_black_theme\">Preto</string>\n    <string name=\"light_theme_settings\">Tema claro</string>\n    <string name=\"dark_theme_settings\">Tema escuro</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Escuro (Material)</string>\n    <string name=\"added_to_favs\">Adicionado aos Favoritos!</string>\n    <string name=\"add_to_favorites\">Adicionar aos Favoritos</string>\n    <string name=\"accounts\">Contas</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Localizações</string>\n    <string name=\"unknown\">Desconhecido</string>\n    <string name=\"removed_from_favs\">Removido dos Favoritos!</string>\n    <string name=\"backup_and_restore\">Becape e Restauração</string>\n    <string name=\"auto_backup\">Backup Automático</string>\n    <string name=\"auto_backup_summary\">A partir do Android 6, o recurso de Backup Automático do Android carregará todas as configurações do aplicativo, dados de login da conta e favoritos no Google Drive, que pode ser restaurado reinstalando o aplicativo após a desinstalação.</string>\n    <string name=\"auto_backup_warning\">Essa preferência não tem efeito se o Google Play Services não estiver presente ou se o Backup Automático estiver desativado das configurações do dispositivo. Desativar aqui não apaga backups existentes.</string>\n    <string name=\"auto_backup_setting\">Ativar Backup Automático</string>\n    <string name=\"manual_backup\">Backup Manual</string>\n    <string name=\"backup_summary\">Faça o becape das configurações de aplicativo do Barinsta, dados de login da conta e/ou favoritos para um texto simples ou arquivo de becape criptografado para restauração posterior.</string>\n    <string name=\"backup_warning\">Se você estiver fazendo o becape dos dados de login da conta, trate o arquivo como confidencial e mantenha-o em algum lugar seguro!</string>\n    <string name=\"create_backup\">Criar novo arquivo de becape</string>\n    <string name=\"restore_backup\">Restaurar arquivo de becape existente</string>\n    <string name=\"file_chosen_label\">Arquivo:</string>\n    <string name=\"enter_password\">Digite a senha</string>\n    <string name=\"select_backup_file\">Selecione um arquivo de becape (.zaai/.backup)</string>\n    <string name=\"apply\">Aplicar</string>\n    <string name=\"save\">Salvar</string>\n    <string name=\"caption\">Legenda</string>\n    <string name=\"edit_caption\">Editar legenda</string>\n    <string name=\"translate_caption\">Traduzir legenda</string>\n    <string name=\"player_timeline_desc\">Barra de progresso do reprodutor de vídeo</string>\n    <string name=\"liking\">Curtindo…</string>\n    <string name=\"like_unsuccessful\">Falha ao curtir</string>\n    <string name=\"unlike_unsuccessful\">Falha ao descurtir</string>\n    <string name=\"unliking\">Descurtindo…</string>\n    <string name=\"controls\">Controles</string>\n    <string name=\"saving\">Salvando…</string>\n    <string name=\"removing\">Removendo…</string>\n    <string name=\"save_unsuccessful\">Falha ao salvar</string>\n    <string name=\"save_remove_unsuccessful\">Falha ao remover</string>\n    <string name=\"downloading\">Baixando…</string>\n    <string name=\"downloader_downloading_child\">Baixar item %1$d de %2$d</string>\n    <string name=\"delete\">Excluir</string>\n    <string name=\"comment\">Comentar</string>\n    <string name=\"layout\">Visualização</string>\n    <string name=\"feed_stories\">Feed de stories</string>\n    <string name=\"opening_post\">Abrindo publicação…</string>\n    <string name=\"share\">Compartilhar</string>\n    <string name=\"layout_style\">Estilo do layout</string>\n    <string name=\"column_count\">Número de colunas</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Mostrar nomes</string>\n    <string name=\"show_avatars\">Mostrar avatares</string>\n    <string name=\"avatar_size\">Tamanho do avatar</string>\n    <string name=\"corners\">Bordas</string>\n    <string name=\"show_grid_gap\">Separar grades</string>\n    <string name=\"post_not_found\">Publicação não encontrada!</string>\n    <string name=\"no_external_app_url\">Nenhum aplicativo encontrado para abrir URLs</string>\n    <string name=\"gallery\">Galeria</string>\n    <string name=\"camera\">Câmera</string>\n    <string name=\"all_photos\">Todas as Fotos</string>\n    <string name=\"all_media\">Todas as Mídias</string>\n    <string name=\"all_videos\">Todos os Vídeos</string>\n    <string name=\"brightness\">Brilho</string>\n    <string name=\"contrast\">Contraste</string>\n    <string name=\"vibrance\">Vibração</string>\n    <string name=\"saturation\">Saturação</string>\n    <string name=\"sharpen\">Nitidez</string>\n    <string name=\"exposure\">Exposição</string>\n    <string name=\"center\">Centralizar</string>\n    <string name=\"color\">Cor</string>\n    <string name=\"start\">Início</string>\n    <string name=\"end\">Fim</string>\n    <string name=\"bilateral_blur\">Desfoque Bilateral</string>\n    <string name=\"vignette\">Vinheta</string>\n    <string name=\"box_blur\">Desfoque da caixa</string>\n    <string name=\"sepia\">Sépia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Resetar</string>\n    <string name=\"crop\">Cortar</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d visualização</item>\n        <item quantity=\"other\">%d visualizações</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">Detalhes</string>\n    <string name=\"title\">Título</string>\n    <string name=\"members\">Membros</string>\n    <string name=\"admin\">Administrador</string>\n    <string name=\"inviter\">Convidador</string>\n    <string name=\"mute_messages\">Silenciar mensagens</string>\n    <string name=\"mute_mentions\">Silenciar menções</string>\n    <string name=\"add_members\">Adicionar membros</string>\n    <string name=\"search\">Buscar</string>\n    <string name=\"done\">Concluído</string>\n    <string name=\"dms_action_make_admin\">Tornar Administrador</string>\n    <string name=\"dms_action_remove_admin\">Remover como Administrador</string>\n    <string name=\"edit_unsuccessful\">Falha ao editar</string>\n    <string name=\"message\">Mensagem</string>\n    <string name=\"tap_to_remove\">Toque para remover</string>\n    <string name=\"forward\">Encaminhar</string>\n    <string name=\"forward_outgoing\">Você encaminhou uma mensagem</string>\n    <string name=\"forward_incoming\">Encaminhou uma mensagem</string>\n    <string name=\"add\">Adicionar</string>\n    <string name=\"send\">Enviar</string>\n    <string name=\"replying_to_yourself\">Em resposta a você</string>\n    <string name=\"replying_to_user\">Em resposta a %s</string>\n    <string name=\"replied_to_yourself\">Você respondeu a si mesmo</string>\n    <string name=\"replied_you\">Você respondeu</string>\n    <string name=\"replied_you_group\">Você respondeu a %s</string>\n    <string name=\"replied_group\">Respondeu a %s</string>\n    <string name=\"replied_to_you\">Respondeu a você</string>\n    <string name=\"replied_to_themself\">Respondeu a si mesmo</string>\n    <string name=\"reacted_story_outgoing\">Você reagiu ao seu story</string>\n    <string name=\"reacted_story_incoming\">Reagiu ao seu story</string>\n    <string name=\"mentioned_story_outgoing\">Você os mencionou em seu story</string>\n    <string name=\"mentioned_story_incoming\">Mencionou você em seu story</string>\n    <string name=\"replied_story_outgoing\">Você respondeu ao seu story</string>\n    <string name=\"replied_story_incoming\">Respondeu ao seu story</string>\n    <string name=\"raven_image_expired\">A imagem expirou</string>\n    <string name=\"raven_image_info\">A imagem expirará quando vista</string>\n    <string name=\"raven_video_expired\">O vídeo expirou</string>\n    <string name=\"raven_video_info\">O vídeo expirará quando visto</string>\n    <string name=\"raven_msg_expired\">A mensagem expirou</string>\n    <string name=\"raven_msg_info\">A mensagem expirará quando vista</string>\n    <string name=\"story_share\">Story de @%s</string>\n    <string name=\"story_share_highlight\">Story em destaque de @%s</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Vídeo</string>\n    <string name=\"voice_message\">Mensagem de voz</string>\n    <string name=\"post\">Publicação</string>\n    <string name=\"approval_required_for_new_members\">Aprovação necessária para entrar</string>\n    <string name=\"requests\">Solicitações</string>\n    <string name=\"admins_only\">Somente administradores</string>\n    <string name=\"added_by\">Adicionado por %s</string>\n    <string name=\"admin_approval_required\">Aprovação do administrador necessária</string>\n    <string name=\"admin_approval_required_description\">Uma aprovação do administrador será necessária para adicionar novos membros ao grupo</string>\n    <string name=\"dms_action_end\">Encerrar conversa</string>\n    <string name=\"dms_action_end_question\">Encerrar conversa?</string>\n    <string name=\"dms_action_end_description\">Todos os membros serão removidos do grupo. Eles ainda serão capazes de ver o histórico da conversa.</string>\n    <string name=\"pending_requests\">Solicitações Pendentes</string>\n    <string name=\"accept_request_from_user\">Aceitar solicitação de %1s (%2s)?</string>\n    <string name=\"decline\">Recusar</string>\n    <string name=\"accept\">Aceitar</string>\n    <string name=\"you\">Você</string>\n    <string name=\"no_pending_requests\">Nenhum pedido pendente</string>\n    <string name=\"checking_for_new_messages\">Verificando se há novas mensagens</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Notificações</string>\n    <string name=\"pref_category_post\">Publicação</string>\n    <string name=\"enable_dm_notifications\">Ativar notificações de DMs</string>\n    <string name=\"enable_dm_auto_refesh\">Atualizar mensagens automaticamente</string>\n    <string name=\"auto_refresh_every\">Atualizar automaticamente a cada</string>\n    <string name=\"secs\">segs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Buscar GIPHY</string>\n    <string name=\"generic_null_response\">A resposta é nula!</string>\n    <string name=\"generic_not_ok_response\">O estado da resposta não está correto!</string>\n    <string name=\"generic_failed_request\">A solicitação falhou!</string>\n    <string name=\"hint_keyword\">Palavra-chave</string>\n    <string name=\"toggle_keyword_filter\">Ativar filtro de palavras-chave</string>\n    <string name=\"edit_keyword_filter\">Editar filtros de palavras-chave</string>\n    <string name=\"added_keywords\">Palavra-chave adicionada: %s para a lista de filtros</string>\n    <string name=\"removed_keywords\">Palavra-chave removida: %s da lista de filtros</string>\n    <string name=\"marked_as_seen\">Marcado como visto</string>\n    <string name=\"delete_unsuccessful\">Falha ao excluir</string>\n    <string name=\"throttle_error\">Impedido pelo Instagram por causa de muitos pedidos da API. Espere algum tempo antes de tentar novamente.</string>\n    <string name=\"error\">Erro</string>\n    <string name=\"account_logged_out\">Esta conta foi desconectada.</string>\n    <string name=\"login_required\">Login necessário!</string>\n    <string name=\"inactive_user\">O usuário está inativo!</string>\n    <string name=\"crash_report_subject\">Relatório de Travamento do Barinsta</string>\n    <string name=\"crash_report_title\">Selecione um aplicativo de e-mail para enviar os registros de travamento</string>\n    <string name=\"not_found\">Não encontrado!</string>\n    <string name=\"rate_limit\">Seu IP foi limitado pelo Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Saiba mais.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Pular esta atualização</string>\n    <string name=\"on_latest_version\">Você já está na versão mais recente</string>\n    <string name=\"tab_order\">Ordem da tela</string>\n    <string name=\"other_tabs\">Outras abas</string>\n    <string name=\"tab_order_start_next_launch\">A ordem das abas será refletida no próximo início</string>\n    <string name=\"dm_remove_warning\">Se salvo, todos os recursos relacionados a DM serão desativados no próximo início</string>\n    <string name=\"copy_caption\">Copiar legenda</string>\n    <string name=\"copy_reply\">Copiar resposta</string>\n    <string name=\"restore\">Restaurar</string>\n    <string name=\"backup\">Becape</string>\n    <string name=\"dir_select_default_message\">Selecione uma pasta onde o Barinsta pode armazenar downloads e arquivos temporários.\\n\\nVocê pode mudar isso mais tarde em Mais &gt; Configurações &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">O Android mudou a forma como os aplicativos podem acessar arquivos e diretórios no armazenamento. Atualmente, o Barinsta não tem permissão para acessar a seguinte pasta:</string>\n    <string name=\"dir_select_permission_revoked_message\">As permissões para a pasta selecionada anteriormente foram revogadas pelo sistema:</string>\n    <string name=\"dir_select_folder_not_exist\">A pasta selecionada anteriormente não existe agora:</string>\n    <string name=\"dir_select_message2\">Selecione novamente o diretório ou selecione um novo diretório clicando no botão abaixo.</string>\n    <string name=\"select_a_folder\">Nenhuma pasta selecionada!</string>\n    <string name=\"dir_select_no_download_folder\">Por favor, escolha um diretório do seu armazenamento, não uma categoria na barra lateral.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Sucesso! Por favor, aguarde. Iniciando o aplicativo…</string>\n    <string name=\"barinsta_folder\">Pasta Barinsta</string>\n    <string name=\"top\">Início</string>\n    <string name=\"recent\">Recente</string>\n    <string name=\"clear\">Limpar</string>\n    <string name=\"no_external_map_app\">Nenhum aplicativo de mapa foi encontrado!</string>\n    <string name=\"click_to_show_full\">Clique para mostrar a contagem completa de curtidas</string>\n    <string name=\"no_profile_pic_found\">Nenhuma foto de perfil encontrada!</string>\n    <string name=\"swipe_up_confirmation\">Tem certeza que quer abrir este link?</string>\n    <string name=\"sending\">Enviando…</string>\n    <string name=\"share_via_dm\">Compartilhar via DM</string>\n    <string name=\"share_link\">Compartilhar link…</string>\n    <string name=\"slide_to_cancel\">Deslize para Cancelar</string>\n    <string name=\"disable_screen_transitions\">Desativar transições de tela</string>\n    <string name=\"invalid_format\">Formato inválido</string>\n    <string name=\"no_directory_picker_activity\">Barinsta não pode iniciar o gerenciador de arquivos do Android. Certifique-se de que ele esteja instalado e ativado em seu dispositivo.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Lista de stories</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ru/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Системный по умолчанию</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Автоматически / Оглядка на систему</item>\n        <item>Автоматически / Оглядка на батарею</item>\n        <item>Тёмная</item>\n        <item>Светлая</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>По умолчанию Instagram (Непрочитанные, затем прочитанные)</item>\n        <item>От новейших до старейших</item>\n        <item>От старейших до новейших</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Нет</item>\n        <item>\\@</item>\n        <item>в</item>\n        <item>на</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>секунды</item>\n        <item>минуты</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">О программе</string>\n    <string name=\"action_dms\">Личные сообщения</string>\n    <string name=\"action_settings\">Настройки</string>\n    <string name=\"action_download\">Скачать</string>\n    <string name=\"action_search\">Поиск имени пользователя…</string>\n    <string name=\"action_compare\">Сравнить</string>\n    <string name=\"clipboard_error\">Ошибка при копировании текста</string>\n    <string name=\"clipboard_copied\">Скопировано в буфер обмена!</string>\n    <string name=\"report\">Отчёт</string>\n    <string name=\"set_password\">Защитить файл паролем</string>\n    <string name=\"password_no_max\">Пароль</string>\n    <string name=\"ok\">Хорошо</string>\n    <string name=\"yes\">Да</string>\n    <string name=\"cancel\">Отмена</string>\n    <string name=\"no\">Нет</string>\n    <string name=\"confirm\">Подтвердить</string>\n    <string name=\"title_favorites\">Избранное</string>\n    <string name=\"title_discover\">Подборка</string>\n    <string name=\"title_comments\">Комментарии</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Активность</string>\n    <string name=\"update_check\">Проверять наличие обновлений при запуске</string>\n    <string name=\"flag_secure\">Блокировать скриншоты &amp; превью приложения</string>\n    <string name=\"download_user_folder\">Скачать публикации в папки с именем пользователя</string>\n    <string name=\"download_prepend_username\">Добавить имя пользователя к имени файла</string>\n    <string name=\"mark_as_seen_setting\">Отметить истории как увиденные после просмотра</string>\n    <string name=\"mark_as_seen_setting_summary\">Автор истории узнает, что вы просмотрели её</string>\n    <string name=\"hide_muted_reels_setting\">Скрыть заглушённые истории из ленты новостей</string>\n    <string name=\"dm_mark_as_seen_setting\">Отметить ЛС как увиденные после просмотра</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Другие участники узнают, что вы просмотрели его</string>\n    <string name=\"autoplay_stories_setting\">Автозапуск видео историй</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Включить уведомления об активности</string>\n    <string name=\"story_sort_setting\">Сортировка историй ленты</string>\n    <string name=\"error_loading_profile\">Ошибка при загрузке профиля! Верно ли имя пользователя? Если это так, то ваш запрос мог быть ограничен сервером.</string>\n    <string name=\"error_loading_hashtag\">Ошибка загрузки хэштэга! Верно ли название?</string>\n    <string name=\"error_loading_location\">Ошибка загрузки локации! Верен ли URL?</string>\n    <string name=\"error_creating_folders\">Ошибка при создании каталога(ов) загрузки.</string>\n    <string name=\"select_folder\">Выбрать папку</string>\n    <string name=\"theme_settings\">Тема</string>\n    <string name=\"select_language\">Язык</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nПубликация</item>\n        <item quantity=\"few\">%s\\nПубликации</item>\n        <item quantity=\"many\">%s\\nПубликаций</item>\n        <item quantity=\"other\">%s\\nПубликации</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Публикация</item>\n        <item quantity=\"few\">%s Публикации</item>\n        <item quantity=\"many\">%s Публикаций</item>\n        <item quantity=\"other\">%s Публикации</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nПодписчик</item>\n        <item quantity=\"few\">%s\\nПодписчиков</item>\n        <item quantity=\"many\">%s\\nПодписчиков</item>\n        <item quantity=\"other\">%s\\nПодписчиков</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nПодписок</string>\n    <string name=\"post_viewer_autoplay_video\">Автовоспроизведение видео</string>\n    <string name=\"post_viewer_background_play\">Воспроизведение видео в фоновом режиме</string>\n    <string name=\"post_viewer_background_play_summary\">Не приостанавливать видео, когда приложение свернуто</string>\n    <string name=\"post_viewer_muted_autoplay\">Всегда заглушать видео</string>\n    <string name=\"post_viewer_show_captions\">Всегда отображать подписи к постам</string>\n    <string name=\"post_viewer_download_dialog_title\">Выберите, что скачивать</string>\n    <string name=\"post_viewer_download_current\">Текущая</string>\n    <string name=\"post_viewer_download_album\">Целый альбом</string>\n    <string name=\"show_stories\">Показать истории</string>\n    <string name=\"no_more_stories\">Нет больше историй!</string>\n    <string name=\"view_post\">Посмотреть публикацию</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Ответ успешно представлен!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d ответ в среднем %s</item>\n        <item quantity=\"few\">%d ответа в среднем %s</item>\n        <item quantity=\"many\">%d ответов в среднем %s</item>\n        <item quantity=\"other\">%d ответы в среднем %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Ваш ответ: %s</string>\n    <string name=\"reply_story\">Ответить на историю</string>\n    <string name=\"reply_hint\">Ответить…</string>\n    <string name=\"story_quiz\">Опрос</string>\n    <string name=\"story_slider\">Ползунок</string>\n    <string name=\"story_quizzed\">Вы уже ответили!</string>\n    <string name=\"story_mentions\">Упоминания</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Это частная учётная запись</string>\n    <string name=\"priv_acc_confirm\">Вы не сможете получить доступ к публикациям после отписки! Вы уверены?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Вы можете войти через Ещё -&gt; Учётная запись в правом нижнем углу или можете просматривать публичные учётные записи без авторизации!</string>\n    <string name=\"empty_acc\">У этой учётной записи нет публикаций</string>\n    <string name=\"empty_list\">Нет таких публикаций!</string>\n    <string name=\"login\">Вход</string>\n    <string name=\"logout\">Выход</string>\n    <string name=\"logout_summary\">Просмотр Instagram анонимно</string>\n    <string name=\"remove_all_acc\">Удалить все учётные записи</string>\n    <string name=\"remove_all_acc_warning\">Это удалит все добавленные учётные записи из приложения!\\nЧтобы удалить только одну учётную запись, нажмите и удерживайте учётную запись в диалоге переключения учётных записей.\\nВы хотите продолжить?</string>\n    <string name=\"time_settings\">Формат даты</string>\n    <string name=\"saved_create_collection\">Создать новую коллекцию</string>\n    <string name=\"edit_collection\">Изменить название коллекции</string>\n    <string name=\"delete_collection\">Удалить коллекцию</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Удалить из коллекции</string>\n    <string name=\"liked\">Понравилось</string>\n    <string name=\"saved\">Сохранено</string>\n    <string name=\"tagged\">Отмечено</string>\n    <string name=\"dm_person\">Сообщение</string>\n    <string name=\"follow\">Подписаться</string>\n    <string name=\"unfollow\">Отписаться</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">В избранное</string>\n    <string name=\"block\">Заблокировать</string>\n    <string name=\"unblock\">Разблокировать</string>\n    <string name=\"restrict\">Ограничить</string>\n    <string name=\"unrestrict\">Снять ограничение</string>\n    <string name=\"mute_stories\">Заглушить истории</string>\n    <string name=\"mute_posts\">Заглушить публикации</string>\n    <string name=\"unmute_stories\">Включить звук историй</string>\n    <string name=\"unmute_posts\">Включить звук публикаций</string>\n    <string name=\"remove_follower\">Удалить подписчика</string>\n    <string name=\"bio_copy\">Копировать биографию</string>\n    <string name=\"bio_translate\">Перевести биографию</string>\n    <string name=\"status_mutual\">Взаимное</string>\n    <string name=\"status_following\">Подписки</string>\n    <string name=\"status_follower\">Подписчик</string>\n    <string name=\"map\">Карта</string>\n    <string name=\"dialog_export_accounts\">Учётные записи</string>\n    <string name=\"dialog_export_settings\">Настройки</string>\n    <string name=\"dialog_export_favorites\">Избранное</string>\n    <string name=\"dialog_import_success\">Успешно импортировано!</string>\n    <string name=\"dialog_import_failed\">Не удалось импортировать!</string>\n    <string name=\"dialog_export_success\">Успешно экспортировано!</string>\n    <string name=\"dialog_export_failed\">Не удалось экспортировать!</string>\n    <string name=\"refresh\">Обновить</string>\n    <string name=\"get_cookies\">Получить файлы cookie</string>\n    <string name=\"time_settings_title_custom\">Использовать пользовательский формат</string>\n    <string name=\"time_settings_title_separator\">Разделитель</string>\n    <string name=\"time_settings_title_time_format\">Формат времени</string>\n    <string name=\"time_settings_title_date_format\">Формат даты</string>\n    <string name=\"time_settings_title_preview\">Предпросмотр</string>\n    <string name=\"time_settings_swap_time\">Поменять местами время и дату</string>\n    <string name=\"quick_access_cannot_delete_curr\">Невозможно удалить текущую учётную запись</string>\n    <string name=\"quick_access_confirm_delete\">Вы уверены, что хотите удалить \\'%s\\'?</string>\n    <string name=\"open_profile\">Открыть профиль</string>\n    <string name=\"view_story\">Просмотр истории</string>\n    <string name=\"view_pfp\">Просмотр изображения профиля</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Неподдерживаемый тип сообщения</string>\n    <string name=\"dms_inbox_unsend\">Отменить отправку сообщения</string>\n    <string name=\"dms_inbox_giphy\">Посмотреть на GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s поделился(-лась) постом @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s поделился(-лась) изображением</string>\n    <string name=\"dms_inbox_shared_video\">%s поделился(-лась) видео</string>\n    <string name=\"dms_inbox_shared_message\">%s прислал(-а) сообщение</string>\n    <string name=\"dms_inbox_shared_gif\">%s поделился(-лась) gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s поделился наклейкой</string>\n    <string name=\"dms_inbox_shared_profile\">%s поделился(-лась) профилем: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s поделился(-лась) локацией: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s поделился(-лась) сохранённой историей @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s поделился(-лась) историей @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s прислал(-а) голосовое сообщение</string>\n    <string name=\"dms_inbox_shared_clip\">%s поделился(-лась) клипом @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s поделился(-лась) видео IGTV @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Вы ответили на историю: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s ответил(-а) на вашу историю: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Вы отреагировали на историю: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s отреагировал(-а) на вашу историю: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Вы упомянули @%s в вашей истории</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s упомянул(-а) вас в истории</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Неизвестный тип материала</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Истёк срок действия медиа!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Доставлено</string>\n    <string name=\"dms_inbox_raven_media_sent\">Отправлено</string>\n    <string name=\"dms_inbox_raven_media_opened\">Открыто</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Повторено</string>\n    <string name=\"dms_inbox_raven_media_sending\">Отправка…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Заблокировано</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Предложено</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Сделан снимок экрана</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Не удаётся доставить</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Нажмите и удерживайте, чтобы записать звук</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Покинуть чат</string>\n    <string name=\"dms_action_leave_question\">Покинуть эту беседу?</string>\n    <string name=\"dms_action_kick\">Выгнать</string>\n    <string name=\"dms_left_users\">Оставшиеся пользователи</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Недействительный пользователь</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram не позволяет загружать видео длиннее 60 секунд для ЛС.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram не позволяет загружать звук длиннее 60 секунд.</string>\n    <string name=\"direct_download_loading\">Получение публикации(ий)</string>\n    <string name=\"downloader_complete\">Скачивание завершено</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Скачивание публикации…</string>\n    <string name=\"downloader_downloading_media\">Загрузка медиафайлов</string>\n    <string name=\"downloader_unknown_error\">Произошла неизвестная ошибка!!!</string>\n    <string name=\"downloader_error_creating_folder\">Ошибка при создании папки!</string>\n    <string name=\"downloader_error_download_file\">Ошибка при скачивании файла</string>\n    <string name=\"comment_viewer_translate_comment\">Перевести комментарий</string>\n    <string name=\"comment_viewer_delete_comment\">Удалить комментарий</string>\n    <string name=\"followers_type_followers\">Подписчики</string>\n    <string name=\"followers_type_following\">Подписки</string>\n    <string name=\"followers_compare\">Сравнение подписчиков &amp; подписок</string>\n    <string name=\"followers_both_following\">Оба подписаны друг на друга</string>\n    <string name=\"followers_not_following\">не подписан на %s</string>\n    <string name=\"followers_not_follower\">%s не следует</string>\n    <string name=\"login_error_loading_cookies\">Ошибка при загрузке cookies</string>\n    <string name=\"comment_hint\">Написать новый комментарий…</string>\n    <string name=\"liked_notif\">Понравилась ваша публикация</string>\n    <string name=\"comment_notif\">Прокомментировал вашу публикацию:</string>\n    <string name=\"follow_notif\">Подписался на вас</string>\n    <string name=\"tagged_notif\">Отметил вас в публикации</string>\n    <string name=\"request_notif\">Запрашивает подписаться на вас</string>\n    <string name=\"request_approve\">Одобрить запрос</string>\n    <string name=\"request_reject\">Отклонить запрос</string>\n    <string name=\"share_public_post\">Поделиться публичной записью с…</string>\n    <string name=\"share_private_post\">Это личная запись! Делитесь с теми, кто может её видеть.</string>\n    <string name=\"discover_empty\">Эта категория каким-то образом пуста…</string>\n    <string name=\"update_available\">Доступно обновление! (%s)</string>\n    <string name=\"updated\">Благодарим Вас за обновление Barinsta!</string>\n    <string name=\"crash_title\">Приложение вылетело</string>\n    <string name=\"crash_descr\">Упс.. приложение вылетело, но не беспокойтесь, вы можете отправить отчёт об ошибке разработчику, чтобы помочь ему исправить эту проблему. (:</string>\n    <string name=\"action_notif\">Активность</string>\n    <string name=\"action_archive\">Архив истории</string>\n    <string name=\"action_ayml\">Предлагаемые пользователи</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"few\">You have %d notifications</item>\n        <item quantity=\"many\">You have %d notifications</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d подписано</string>\n    <string name=\"activity_count_comments\">%d комментариев</string>\n    <string name=\"activity_count_commentlikes\">%d симпатий к комментарию</string>\n    <string name=\"activity_count_usertags\">%d меток пользователя</string>\n    <string name=\"activity_count_likes\">%d нравится</string>\n    <string name=\"activity_count_poy\">%d фотографий с Вами</string>\n    <string name=\"activity_count_requests\">%d запросов на подписку</string>\n    <string name=\"activity_notloggedin\">Вы вышли из системы перед нажатием на это уведомление?!</string>\n    <string name=\"feed\">Лента новостей</string>\n    <string name=\"profile\">Профиль</string>\n    <string name=\"more\">Ещё</string>\n    <string name=\"title_dm\">ЛС</string>\n    <string name=\"number_selected\">%d выбрано</string>\n    <string name=\"logout_success\">Успешный выход из системы!</string>\n    <string name=\"dm_thread_info\">Информация</string>\n    <string name=\"mark_as_seen\">Отметить как просмотренное</string>\n    <string name=\"version\">Версия</string>\n    <string name=\"pref_start_screen\">Начальный экран</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Показывать клавиатуру при поиске</string>\n    <string name=\"pref_category_general\">Общее</string>\n    <string name=\"pref_category_theme\">Тема</string>\n    <string name=\"pref_category_downloads\">Загрузки</string>\n    <string name=\"pref_category_locale\">Локализация</string>\n    <string name=\"account\">Учётная запись</string>\n    <string name=\"account_hint\">Текущий вход не работает? Просто добавьте учётную запись снова.</string>\n    <string name=\"add_account\">Добавить учётную запись</string>\n    <string name=\"about_category_license\">Лицензия (только на английском)</string>\n    <string name=\"about_documentation\">Посетить наш сайт</string>\n    <string name=\"about_documentation_summary\">Получайте поддержку, обсуждайте, встречайтесь с другими и развлекайтесь!</string>\n    <string name=\"about_repository\">Посмотреть наш исходный код на GitHub</string>\n    <string name=\"about_repository_summary\">Проводите ревизию, сохранения, сообщайте от ошибках, вносите свой вклад и веселитесь (снова)!</string>\n    <string name=\"about_feedback\">Отправить отзыв по электронной почте</string>\n    <string name=\"about_category_3pt\">Атрибуты сторонних производителей</string>\n    <string name=\"reminder\">Напоминание</string>\n    <string name=\"reminder_summary\">Пожалуйста, используйте это приложение ответственно. Скачанные изображения должны использоваться только в тех целях, которые разрешены действующим законодательством.</string>\n    <string name=\"light_white_theme\">Белая</string>\n    <string name=\"dark_black_theme\">Чёрная</string>\n    <string name=\"light_theme_settings\">Светлая тема</string>\n    <string name=\"dark_theme_settings\">Тёмная тема</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Бариста</string>\n    <string name=\"dark_material_dark_theme\">Тёмный \\\"осязаемый\\\"</string>\n    <string name=\"added_to_favs\">Добавлено в избранное!</string>\n    <string name=\"add_to_favorites\">Добавить в избранное</string>\n    <string name=\"accounts\">Учётные записи</string>\n    <string name=\"hashtags\">Хэштеги</string>\n    <string name=\"locations\">Местоположения</string>\n    <string name=\"unknown\">Неизвестный</string>\n    <string name=\"removed_from_favs\">Удалено из избранного!</string>\n    <string name=\"backup_and_restore\">Резервное копирование &amp; Восстановление</string>\n    <string name=\"auto_backup\">Автобэкап</string>\n    <string name=\"auto_backup_summary\">Начиная с Android 6, функция автоматического резервного копирования Android будет загружать все настройки приложения, данные для входа в аккаунт, и избранное на диск Google, которые можно восстановить, переустановив приложение после удаления.</string>\n    <string name=\"auto_backup_warning\">Эта настройка не имеет эффекта, если службы Google Play отсутствуют, или если Автобэкап отключен в настройках вашего устройства. Отключение не стирает существующие резервные копии.</string>\n    <string name=\"auto_backup_setting\">Включить Автобэкап</string>\n    <string name=\"manual_backup\">Ручной бэкап</string>\n    <string name=\"backup_summary\">Резервное копирование настроек приложения Barinsta, учётных данных, и/или избранного в виде обычного текста или зашифрованного файла резервной копии для последующего восстановления.</string>\n    <string name=\"backup_warning\">Если вы делаете резервное копирование данных для входа в учётную запись, считайте файл конфиденциальным и храните его где-то в безопасности!</string>\n    <string name=\"create_backup\">Создать новый файл резервной копии</string>\n    <string name=\"restore_backup\">Восстановить из существующего файла резервной копии</string>\n    <string name=\"file_chosen_label\">Файл:</string>\n    <string name=\"enter_password\">Введите пароль</string>\n    <string name=\"select_backup_file\">Выберите файл резервной копии (.zaai/.backup)</string>\n    <string name=\"apply\">Применить</string>\n    <string name=\"save\">Сохранить</string>\n    <string name=\"caption\">Заголовок</string>\n    <string name=\"edit_caption\">Изменить подпись</string>\n    <string name=\"translate_caption\">Перевести подпись</string>\n    <string name=\"player_timeline_desc\">Лента времени видеопроигрывателя</string>\n    <string name=\"liking\">Нравится…</string>\n    <string name=\"like_unsuccessful\">Не удалось поставить лайк</string>\n    <string name=\"unlike_unsuccessful\">Не удалось снять лайк</string>\n    <string name=\"unliking\">Отклонение…</string>\n    <string name=\"controls\">Управление</string>\n    <string name=\"saving\">Сохранение…</string>\n    <string name=\"removing\">Удаление…</string>\n    <string name=\"save_unsuccessful\">Не удалось сохранить</string>\n    <string name=\"save_remove_unsuccessful\">Не удалось удалить</string>\n    <string name=\"downloading\">Скачивание…</string>\n    <string name=\"downloader_downloading_child\">Скачать элемент %1$d из %2$d</string>\n    <string name=\"delete\">Удалить</string>\n    <string name=\"comment\">Комментировать</string>\n    <string name=\"layout\">Формат</string>\n    <string name=\"feed_stories\">Истории ленты новостей</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Поделиться</string>\n    <string name=\"layout_style\">Внешний вид</string>\n    <string name=\"column_count\">Количество столбцов</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Показать имена</string>\n    <string name=\"show_avatars\">Показывать аватары</string>\n    <string name=\"avatar_size\">Размер аватара</string>\n    <string name=\"corners\">Углы</string>\n    <string name=\"show_grid_gap\">Показать разрыв сетки</string>\n    <string name=\"post_not_found\">Публикация не найдена!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Галерея</string>\n    <string name=\"camera\">Камера</string>\n    <string name=\"all_photos\">Все фото</string>\n    <string name=\"all_media\">Все медиа-файлы</string>\n    <string name=\"all_videos\">Все видео</string>\n    <string name=\"brightness\">Яркость</string>\n    <string name=\"contrast\">Контрастность</string>\n    <string name=\"vibrance\">Красочность</string>\n    <string name=\"saturation\">Насыщенность</string>\n    <string name=\"sharpen\">Резкость</string>\n    <string name=\"exposure\">Выдержка</string>\n    <string name=\"center\">Центр</string>\n    <string name=\"color\">Цвет</string>\n    <string name=\"start\">Начало</string>\n    <string name=\"end\">Конец</string>\n    <string name=\"bilateral_blur\">Двустороннее размытие</string>\n    <string name=\"vignette\">Виньетка</string>\n    <string name=\"box_blur\">Размытие</string>\n    <string name=\"sepia\">Сепия</string>\n    <string name=\"clarendon\">Кларендон</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Аден</string>\n    <string name=\"reset\">Сброс</string>\n    <string name=\"crop\">Кадрировать</string>\n    <string name=\"normal\">Исходный</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d просмотр</item>\n        <item quantity=\"few\">%d просмотра</item>\n        <item quantity=\"many\">%d просмотров</item>\n        <item quantity=\"other\">%d просмотров</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s история</item>\n        <item quantity=\"few\">%s истории</item>\n        <item quantity=\"many\">%s историй</item>\n        <item quantity=\"other\">%s истории</item>\n    </plurals>\n    <string name=\"details\">Подробнее</string>\n    <string name=\"title\">Заголовок</string>\n    <string name=\"members\">Участники</string>\n    <string name=\"admin\">Администратор</string>\n    <string name=\"inviter\">Пригласивший</string>\n    <string name=\"mute_messages\">Отключить уведомления</string>\n    <string name=\"mute_mentions\">Заглушить упоминания</string>\n    <string name=\"add_members\">Добавить участников</string>\n    <string name=\"search\">Поиск</string>\n    <string name=\"done\">Готово</string>\n    <string name=\"dms_action_make_admin\">Сделать администратором</string>\n    <string name=\"dms_action_remove_admin\">Удалить администратора</string>\n    <string name=\"edit_unsuccessful\">Не удалось изменить</string>\n    <string name=\"message\">Сообщение</string>\n    <string name=\"tap_to_remove\">Нажмите для удаления</string>\n    <string name=\"forward\">Переслать</string>\n    <string name=\"forward_outgoing\">Вы переслали сообщение</string>\n    <string name=\"forward_incoming\">Переслано сообщение</string>\n    <string name=\"add\">Добавить</string>\n    <string name=\"send\">Отправить</string>\n    <string name=\"replying_to_yourself\">Ответ себе</string>\n    <string name=\"replying_to_user\">Ответить %s</string>\n    <string name=\"replied_to_yourself\">Вы ответили себе</string>\n    <string name=\"replied_you\">Вы ответили</string>\n    <string name=\"replied_you_group\">Вы ответили %s</string>\n    <string name=\"replied_group\">Отвечено %s</string>\n    <string name=\"replied_to_you\">Вам отвечено</string>\n    <string name=\"replied_to_themself\">Отвечено себе</string>\n    <string name=\"reacted_story_outgoing\">Вы отреагировали на их историю</string>\n    <string name=\"reacted_story_incoming\">Отреагировал на Вашу историю</string>\n    <string name=\"mentioned_story_outgoing\">Вы упомянули их в Вашей истории</string>\n    <string name=\"mentioned_story_incoming\">Упомянули Вас в их истории</string>\n    <string name=\"replied_story_outgoing\">Вы ответили на их историю</string>\n    <string name=\"replied_story_incoming\">Ответил(а) на Вашу историю</string>\n    <string name=\"raven_image_expired\">Истёк срок действия изображения</string>\n    <string name=\"raven_image_info\">Срок действия изображения истечёт, будучи увиденным</string>\n    <string name=\"raven_video_expired\">Срок действия видео истёк</string>\n    <string name=\"raven_video_info\">Срок действия видео истечёт, будучи увиденным</string>\n    <string name=\"raven_msg_expired\">Истёк срок действия сообщения</string>\n    <string name=\"raven_msg_info\">Срок действия сообщения истечёт, будучи увиденным</string>\n    <string name=\"story_share\">История @%s</string>\n    <string name=\"story_share_highlight\">Основные моменты истории @%s</string>\n    <string name=\"photo\">Фото</string>\n    <string name=\"video\">Видео</string>\n    <string name=\"voice_message\">Голосовое сообщение</string>\n    <string name=\"post\">Опубликовать</string>\n    <string name=\"approval_required_for_new_members\">Требуется подтверждение администратора</string>\n    <string name=\"requests\">Заявки</string>\n    <string name=\"admins_only\">Только администраторы</string>\n    <string name=\"added_by\">Добавил %s</string>\n    <string name=\"admin_approval_required\">Требуется подтверждение администратора</string>\n    <string name=\"admin_approval_required_description\">Для добавления новых участников потребуется подтверждение администратора</string>\n    <string name=\"dms_action_end\">Закрыть чат</string>\n    <string name=\"dms_action_end_question\">Закрыть чат?</string>\n    <string name=\"dms_action_end_description\">Все участники будут удалены из группы. Они смогут просматривать историю чата.</string>\n    <string name=\"pending_requests\">Ожидающие заявки</string>\n    <string name=\"accept_request_from_user\">Принять запрос от %1s (%2s)?</string>\n    <string name=\"decline\">Отклонить</string>\n    <string name=\"accept\">Принять</string>\n    <string name=\"you\">Вы</string>\n    <string name=\"no_pending_requests\">Нет ожидающих запросов</string>\n    <string name=\"checking_for_new_messages\">Проверка наличия новых сообщений</string>\n    <string name=\"pref_category_stories\">Истории</string>\n    <string name=\"pref_category_dm\">ЛС</string>\n    <string name=\"pref_category_notifications\">Уведомления</string>\n    <string name=\"pref_category_post\">Публикация</string>\n    <string name=\"enable_dm_notifications\">Включить уведомления ЛС</string>\n    <string name=\"enable_dm_auto_refesh\">Автоматическое обновление сообщений</string>\n    <string name=\"auto_refresh_every\">Автоматическое обновление каждые</string>\n    <string name=\"secs\">секунды</string>\n    <string name=\"mins\">минуты</string>\n    <string name=\"search_giphy\">Поиск GIPHY</string>\n    <string name=\"generic_null_response\">Ответ пуст!</string>\n    <string name=\"generic_not_ok_response\">Ответный статус не в порядке!</string>\n    <string name=\"generic_failed_request\">Запрос не удался!</string>\n    <string name=\"hint_keyword\">Ключевое слово</string>\n    <string name=\"toggle_keyword_filter\">Включить фильтр по ключевым словам</string>\n    <string name=\"edit_keyword_filter\">Редактировать фильтры ключевых слов</string>\n    <string name=\"added_keywords\">Добавлено ключевое слово: %s в список фильтров</string>\n    <string name=\"removed_keywords\">Удалено ключевое слово: %s из списка фильтров</string>\n    <string name=\"marked_as_seen\">Отмечено как просмотренное</string>\n    <string name=\"delete_unsuccessful\">Не удалось удалить</string>\n    <string name=\"throttle_error\">Замято Instagram\\'ом из-за слишком большого количества запросов API. Подождите некоторое время перед повторной попыткой.</string>\n    <string name=\"error\">Ошибка</string>\n    <string name=\"account_logged_out\">Эта учётная запись вышла из системы.</string>\n    <string name=\"login_required\">Требуется вход в систему!</string>\n    <string name=\"inactive_user\">Пользователь неактивен!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Выберите приложение для отправки логов ошибки</string>\n    <string name=\"not_found\">Не найдено!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Пропустить это обновление</string>\n    <string name=\"on_latest_version\">Вы уже используете последнюю версию</string>\n    <string name=\"tab_order\">Порядок экрана</string>\n    <string name=\"other_tabs\">Другие вкладки</string>\n    <string name=\"tab_order_start_next_launch\">Порядок вкладок будет отражён при следующем запуске</string>\n    <string name=\"dm_remove_warning\">При сохранении, все функции, связанные с ЛС, будут отключены при следующем запуске</string>\n    <string name=\"copy_caption\">Копировать подпись</string>\n    <string name=\"copy_reply\">Копировать ответ</string>\n    <string name=\"restore\">Восстановление</string>\n    <string name=\"backup\">Резервное копирование</string>\n    <string name=\"dir_select_default_message\">Выберите папку, куда Barinsta следует сохранять загрузки и временные файлы.\\n\\nВы сможете изменить это позже в меню Ещё &gt; Настройки &gt; Загрузки.</string>\n    <string name=\"dir_select_reselect_message\">Android изменил способ доступа к файлам и каталогам на накопителе. В настоящее время Barinsta не имеет разрешения на доступ к следующей папке:</string>\n    <string name=\"dir_select_permission_revoked_message\">Разрешения для ранее выбранной папки были отозваны системой:</string>\n    <string name=\"dir_select_folder_not_exist\">Выбранная ранее папка не существует:</string>\n    <string name=\"dir_select_message2\">Выберите папку заново, нажав на кнопку ниже.</string>\n    <string name=\"select_a_folder\">Папка не выбрана!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Успех! Пожалуйста, подождите. Приложение запускается…</string>\n    <string name=\"barinsta_folder\">Папка Barinsta</string>\n    <string name=\"top\">В лидерах</string>\n    <string name=\"recent\">Недавние</string>\n    <string name=\"clear\">Очистить</string>\n    <string name=\"no_external_map_app\">Не найдено картографического приложения!</string>\n    <string name=\"click_to_show_full\">Нажмите, чтобы показать полное число лайков</string>\n    <string name=\"no_profile_pic_found\">Картинка профиля не найдена!</string>\n    <string name=\"swipe_up_confirmation\">Вы действительно хотите открыть эту ссылку?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Поделиться в ЛС</string>\n    <string name=\"share_link\">Поделиться ссылкой…</string>\n    <string name=\"slide_to_cancel\">Сдвиньте для отмены</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sk/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Prednastavené systémom</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Automaticky / podľa systému</item>\n        <item>Automaticky / podľa batérie</item>\n        <item>Tmavá</item>\n        <item>Svetlá</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Ako Instagram (Najprv neprečítané potom prečítané)</item>\n        <item>Od najnovších po najstaršie</item>\n        <item>Od najstarších po najnovšie</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Žiadna</item>\n        <item>\\@</item>\n        <item>o</item>\n        <item>v</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>sekúnd</item>\n        <item>minút</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">O aplikácii</string>\n    <string name=\"action_dms\">Správy</string>\n    <string name=\"action_settings\">Nastavenia</string>\n    <string name=\"action_download\">Stiahnuť</string>\n    <string name=\"action_search\">Hľadať používateľa…</string>\n    <string name=\"action_compare\">Porovnať</string>\n    <string name=\"clipboard_error\">Chyba pri kopírovaní textu</string>\n    <string name=\"clipboard_copied\">Skopírované do schránky!</string>\n    <string name=\"report\">Nahlásiť</string>\n    <string name=\"set_password\">Uzamknúť súbor s heslom</string>\n    <string name=\"password_no_max\">Heslo</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Áno</string>\n    <string name=\"cancel\">Zrušiť</string>\n    <string name=\"no\">Nie</string>\n    <string name=\"confirm\">Potvrdiť</string>\n    <string name=\"title_favorites\">Obľúbené</string>\n    <string name=\"title_discover\">Prezerať</string>\n    <string name=\"title_comments\">Komentáre</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Aktivita</string>\n    <string name=\"update_check\">Kontrolovať aktualizácie pri štarte</string>\n    <string name=\"flag_secure\">Block screenshots &amp; app preview</string>\n    <string name=\"download_user_folder\">Ukľadať do priečinkov podľa mena</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Označiť príbehy po videní ako videné</string>\n    <string name=\"mark_as_seen_setting_summary\">Autor príbehu bude vedieť že ste ho videli</string>\n    <string name=\"hide_muted_reels_setting\">Hide muted stories from feed</string>\n    <string name=\"dm_mark_as_seen_setting\">Po prečítaní, označiť správu ako prečítanú</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Ostatní budú vedieť že ste ho videli</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Zapnúť notifikácie o aktivitách</string>\n    <string name=\"story_sort_setting\">Zoradenie príbehov</string>\n    <string name=\"error_loading_profile\">Chyba pri načítavaní profilu! Je používateľské meno správne? Ak áno môžete byť obmedzený limitami Instagramu.</string>\n    <string name=\"error_loading_hashtag\">Chyba pri načítavaní hashtagu! Je meno správne?</string>\n    <string name=\"error_loading_location\">Chyba pri načítavaní lokácie! Je URL správna?</string>\n    <string name=\"error_creating_folders\">Chyba pri vytvaraní priečinku(ov).</string>\n    <string name=\"select_folder\">Vybrať priečinok</string>\n    <string name=\"theme_settings\">Téma</string>\n    <string name=\"select_language\">Jazyk</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPríspevok</item>\n        <item quantity=\"few\">%s\\nPríspevky</item>\n        <item quantity=\"many\">%s\\nPríspevku</item>\n        <item quantity=\"other\">%s\\nPríspevkov</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Príspevok</item>\n        <item quantity=\"few\">%s Príspevky</item>\n        <item quantity=\"many\">%s Príspevku</item>\n        <item quantity=\"other\">%s Príspevkov</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nSledovateľ</item>\n        <item quantity=\"few\">%s\\nSledovatelia</item>\n        <item quantity=\"many\">%s\\nSledovateľa</item>\n        <item quantity=\"other\">%s\\nSledovateľov</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nSleduje</string>\n    <string name=\"post_viewer_autoplay_video\">Automaticky prehrávať videá</string>\n    <string name=\"post_viewer_background_play\">Continue videos in background</string>\n    <string name=\"post_viewer_background_play_summary\">Do not pause videos when the app is out of focus</string>\n    <string name=\"post_viewer_muted_autoplay\">Vždy stíšiť videá</string>\n    <string name=\"post_viewer_show_captions\">Vždy zobraziť popis príspevku</string>\n    <string name=\"post_viewer_download_dialog_title\">Vybrať čo stiahnuť</string>\n    <string name=\"post_viewer_download_current\">Aktuálne</string>\n    <string name=\"post_viewer_download_album\">Celý album</string>\n    <string name=\"show_stories\">Zobraziť príbehy</string>\n    <string name=\"no_more_stories\">Žiadne ďalšie príbehy!</string>\n    <string name=\"view_post\">Zobraziť príspevok</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Odpoveď úspešná!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d odpoveď s výsledkom %s</item>\n        <item quantity=\"few\">%d odpovede s výsledkom %s</item>\n        <item quantity=\"many\">%d odpovede s výsledkom %s</item>\n        <item quantity=\"other\">%d odpovedí s výsledkom %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Tvoja odpoveď: %s</string>\n    <string name=\"reply_story\">Odpovedať na príbeh</string>\n    <string name=\"reply_hint\">Odpovedať…</string>\n    <string name=\"story_quiz\">Kvíz</string>\n    <string name=\"story_slider\">Posúvač</string>\n    <string name=\"story_quizzed\">Už si hlasoval!</string>\n    <string name=\"story_mentions\">Zmienky</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Tento účet je súkromný</string>\n    <string name=\"priv_acc_confirm\">Nebudeš môcť pristúpiť k príspevkom po zrušení sledovania. Si si istý?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Môžete sa prihlásiť pomocou Viac -&gt; Účet na dolnom pravom rohu alebo pokračovať v sledovaní verejných profilov bez prihlásenia!</string>\n    <string name=\"empty_acc\">Tento účet nemá žiadne príspevky</string>\n    <string name=\"empty_list\">Žiadne príspevky!</string>\n    <string name=\"login\">Prihlásiť sa</string>\n    <string name=\"logout\">Odhlásiť sa</string>\n    <string name=\"logout_summary\">Prehliadať Instagram anonymne</string>\n    <string name=\"remove_all_acc\">Odstrániť všetky účty</string>\n    <string name=\"remove_all_acc_warning\">Táto možnosť vymaže všetky účty z aplikácie!\\nAk chcete odstrániť iba jeden účet, dlhšie podržte na účte v meniči účtov.\\nChcete pokračovať?</string>\n    <string name=\"time_settings\">Formát dátumu</string>\n    <string name=\"saved_create_collection\">Vytvoriť novú kolekciu</string>\n    <string name=\"edit_collection\">Upraviť meno kolekcie</string>\n    <string name=\"delete_collection\">Vymazať kolekciu</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Remove from collection</string>\n    <string name=\"liked\">Obľúbené</string>\n    <string name=\"saved\">Uložené</string>\n    <string name=\"tagged\">Označené</string>\n    <string name=\"dm_person\">Správa</string>\n    <string name=\"follow\">Sledovať</string>\n    <string name=\"unfollow\">Zrušiť sledovanie</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Obľúbené</string>\n    <string name=\"block\">Blokovať</string>\n    <string name=\"unblock\">Odblokovať</string>\n    <string name=\"restrict\">Obmedziť</string>\n    <string name=\"unrestrict\">Zrušiť obmedzenie</string>\n    <string name=\"mute_stories\">Mute stories</string>\n    <string name=\"mute_posts\">Mute posts</string>\n    <string name=\"unmute_stories\">Unmute stories</string>\n    <string name=\"unmute_posts\">Unmute posts</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Skopírovať bio</string>\n    <string name=\"bio_translate\">Preložiť bio</string>\n    <string name=\"status_mutual\">Spoločné</string>\n    <string name=\"status_following\">Sledované</string>\n    <string name=\"status_follower\">Sledovateľ</string>\n    <string name=\"map\">Mapa</string>\n    <string name=\"dialog_export_accounts\">Účty</string>\n    <string name=\"dialog_export_settings\">Nastavenia</string>\n    <string name=\"dialog_export_favorites\">Obľúbené</string>\n    <string name=\"dialog_import_success\">Úspešne importované!</string>\n    <string name=\"dialog_import_failed\">Import zlyhal!</string>\n    <string name=\"dialog_export_success\">Úspešne exportované!</string>\n    <string name=\"dialog_export_failed\">Export zlyhal!</string>\n    <string name=\"refresh\">Obnoviť</string>\n    <string name=\"get_cookies\">Získať cookies</string>\n    <string name=\"time_settings_title_custom\">Použiť vlastný formát</string>\n    <string name=\"time_settings_title_separator\">Oddeľovač</string>\n    <string name=\"time_settings_title_time_format\">Formát času</string>\n    <string name=\"time_settings_title_date_format\">Formát dátumu</string>\n    <string name=\"time_settings_title_preview\">Náhľad</string>\n    <string name=\"time_settings_swap_time\">Vymeniť pozíciu času a dátumu</string>\n    <string name=\"quick_access_cannot_delete_curr\">Nemôžete vymazať účet ktorý sa momentálne používa</string>\n    <string name=\"quick_access_confirm_delete\">Naozaj chcete odstrániť \\'%s\\'?</string>\n    <string name=\"open_profile\">Otvoriť profil</string>\n    <string name=\"view_story\">Zobraziť príbeh</string>\n    <string name=\"view_pfp\">Zobraziť profilovú fotografiu</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Unsupported message type</string>\n    <string name=\"dms_inbox_unsend\">Zrušiť odoslanie správy</string>\n    <string name=\"dms_inbox_giphy\">View on GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Neznámy typ média</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Médium expirovalo!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Doručené</string>\n    <string name=\"dms_inbox_raven_media_sent\">Odoslané</string>\n    <string name=\"dms_inbox_raven_media_opened\">Otvorené</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Opakovane prehraté</string>\n    <string name=\"dms_inbox_raven_media_sending\">Odosiela sa…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blokované</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Navrhnuté</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Urobená snímka obrazovky</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Nedá sa odoslať</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Press and hold to record audio</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Opustiť chat</string>\n    <string name=\"dms_action_leave_question\">Opustiť tento chat?</string>\n    <string name=\"dms_action_kick\">Vykopnúť</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Získavam príspevky</string>\n    <string name=\"downloader_complete\">Sťahovanie bolo dokončené</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Sťahujem príspevok…</string>\n    <string name=\"downloader_downloading_media\">Sťahujem médiá</string>\n    <string name=\"downloader_unknown_error\">Vyskytla sa neznáma chyba</string>\n    <string name=\"downloader_error_creating_folder\">Chyba pri vytváraní priečinka!</string>\n    <string name=\"downloader_error_download_file\">Chyba pri sťahovaní súboru</string>\n    <string name=\"comment_viewer_translate_comment\">Preložiť komentár</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Sledovatelia</string>\n    <string name=\"followers_type_following\">Sleduje</string>\n    <string name=\"followers_compare\">Porovnanie sledovaných &amp; sledujúcich</string>\n    <string name=\"followers_both_following\">Sledujú sa recipročne</string>\n    <string name=\"followers_not_following\">nesleduje %s</string>\n    <string name=\"followers_not_follower\">%s nesleduje</string>\n    <string name=\"login_error_loading_cookies\">Chyba pri načítavaní cookies</string>\n    <string name=\"comment_hint\">Napísať komentár…</string>\n    <string name=\"liked_notif\">Označil/a že sa mu páči váš príspevok</string>\n    <string name=\"comment_notif\">Komentoval tvoj príspevok:</string>\n    <string name=\"follow_notif\">Ťa začal sledovať</string>\n    <string name=\"tagged_notif\">Označil ťa na príspevku</string>\n    <string name=\"request_notif\">Chce ťa začať sledovať</string>\n    <string name=\"request_approve\">Schváliť žiadosť</string>\n    <string name=\"request_reject\">Odmietnuť žiadosť</string>\n    <string name=\"share_public_post\">Zdieľať tento príspevok…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">Táto kategória je nejaká prázdna…</string>\n    <string name=\"update_available\">Aktualizácia je pripravená! (%s)</string>\n    <string name=\"updated\">Ďakujeme za aktualizovanie programu Barinsta!</string>\n    <string name=\"crash_title\">Aplikácia spadla</string>\n    <string name=\"crash_descr\">Ups... aplikácia spadla, ale neboj sa, m§žeš poslať chybové hlásenie vývojárom a pomôcť im opraviť túto chybu. (:</string>\n    <string name=\"action_notif\">Aktivita</string>\n    <string name=\"action_archive\">Archív príbehov</string>\n    <string name=\"action_ayml\">Používatelia ktorých možno poznáte</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"few\">You have %d notifications</item>\n        <item quantity=\"many\">You have %d notifications</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d sledovaní</string>\n    <string name=\"activity_count_comments\">%d komentárov</string>\n    <string name=\"activity_count_commentlikes\">%d komentárov ktoré sa niekomu páčia</string>\n    <string name=\"activity_count_usertags\">%d označení</string>\n    <string name=\"activity_count_likes\">%d označení \\\"páči sa mi to\\\"</string>\n    <string name=\"activity_count_poy\">%d photos of you</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">Odhlásili ste sa pred kliknutím tejto notifikácie?</string>\n    <string name=\"feed\">Novinky</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Viac</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d vybraných</string>\n    <string name=\"logout_success\">Úspešne odhlásený/á!</string>\n    <string name=\"dm_thread_info\">Informácie</string>\n    <string name=\"mark_as_seen\">Označiť ako videné</string>\n    <string name=\"version\">Verzia</string>\n    <string name=\"pref_start_screen\">Úvodná obrazovka</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">Všeobecné</string>\n    <string name=\"pref_category_theme\">Téma</string>\n    <string name=\"pref_category_downloads\">Stiahnuté</string>\n    <string name=\"pref_category_locale\">Jazyk</string>\n    <string name=\"account\">Účet</string>\n    <string name=\"account_hint\">Nefunguje prihlásenie? Pridajte účet znova.</string>\n    <string name=\"add_account\">Pridať účet</string>\n    <string name=\"about_category_license\">Licencia (V angličtine)</string>\n    <string name=\"about_documentation\">Navštívte našu stránku</string>\n    <string name=\"about_documentation_summary\">Získaj pomoc, diskutuj, spoznávaj a zabav sa!</string>\n    <string name=\"about_repository\">Zobraziť zdrojový kód na GitHube</string>\n    <string name=\"about_repository_summary\">Pomôž, nahlasuj chyby a zabav sa (znova)!</string>\n    <string name=\"about_feedback\">Odoslať spätnú väzbu na email</string>\n    <string name=\"about_category_3pt\">Zdroje tretích strán</string>\n    <string name=\"reminder\">Pripomienka</string>\n    <string name=\"reminder_summary\">Prosím používajte aplikáciu zodpovedne. Sťahované obrázky môžu byť použité len v prípadoch v ktorých to dovoľuje zákon.</string>\n    <string name=\"light_white_theme\">Biela</string>\n    <string name=\"dark_black_theme\">Čierna</string>\n    <string name=\"light_theme_settings\">Biela téma</string>\n    <string name=\"dark_theme_settings\">Tmavá téma</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Pridané do obľúbených!</string>\n    <string name=\"add_to_favorites\">Pridať do obľúbených</string>\n    <string name=\"accounts\">Účty</string>\n    <string name=\"hashtags\">Hashtagy</string>\n    <string name=\"locations\">Miesta</string>\n    <string name=\"unknown\">Neznáme</string>\n    <string name=\"removed_from_favs\">Odstrániť z obľúbených!</string>\n    <string name=\"backup_and_restore\">Backup &amp; Restore</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Vytvoriť súbor zálohy</string>\n    <string name=\"restore_backup\">Obnoviť z existujúceho súboru zálohy</string>\n    <string name=\"file_chosen_label\">Súbor:</string>\n    <string name=\"enter_password\">Zadajte heslo</string>\n    <string name=\"select_backup_file\">Zvoliť súbor zálohy (.zaai/.backup)</string>\n    <string name=\"apply\">Použiť</string>\n    <string name=\"save\">Uložiť</string>\n    <string name=\"caption\">Popis</string>\n    <string name=\"edit_caption\">Upraviť popis</string>\n    <string name=\"translate_caption\">Preložiť popis</string>\n    <string name=\"player_timeline_desc\">Ovládanie videa</string>\n    <string name=\"liking\">Páči sa mi to…</string>\n    <string name=\"like_unsuccessful\">\\\"Páči sa mi to\\\" neúspešné</string>\n    <string name=\"unlike_unsuccessful\">\\\"Nepáči sa mi to\\\" neúspešné</string>\n    <string name=\"unliking\">Odstraňujem \\\"páči sa mi to\\\"…</string>\n    <string name=\"controls\">Ovládanie</string>\n    <string name=\"saving\">Ukladám…</string>\n    <string name=\"removing\">Odstraňujem…</string>\n    <string name=\"save_unsuccessful\">Ukladanie nebolo úspešné</string>\n    <string name=\"save_remove_unsuccessful\">Odstránenie nebolo úspešné</string>\n    <string name=\"downloading\">Sťahujem…</string>\n    <string name=\"downloader_downloading_child\">Sťahuje sa %1$d z %2$d</string>\n    <string name=\"delete\">Odstrániť</string>\n    <string name=\"comment\">Komentovať</string>\n    <string name=\"layout\">Rozloženie</string>\n    <string name=\"feed_stories\">Príbehy</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Zdieľať</string>\n    <string name=\"layout_style\">Štýl rozloženia</string>\n    <string name=\"column_count\">Počet stĺpcov</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Zobraziť mená</string>\n    <string name=\"show_avatars\">Zobraziť avatary</string>\n    <string name=\"avatar_size\">Veľkosť avataru</string>\n    <string name=\"corners\">Rohy</string>\n    <string name=\"show_grid_gap\">Zobraziť medzery v mriežke</string>\n    <string name=\"post_not_found\">Príspevok sa nenašiel!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galéria</string>\n    <string name=\"camera\">Kamera</string>\n    <string name=\"all_photos\">Všetky fotky</string>\n    <string name=\"all_media\">Všetky média</string>\n    <string name=\"all_videos\">Všetky videá</string>\n    <string name=\"brightness\">Jas</string>\n    <string name=\"contrast\">Kontrast</string>\n    <string name=\"vibrance\">Živosť</string>\n    <string name=\"saturation\">Saturácia</string>\n    <string name=\"sharpen\">Zaostrenie</string>\n    <string name=\"exposure\">Expozícia</string>\n    <string name=\"center\">Centrovať</string>\n    <string name=\"color\">Farba</string>\n    <string name=\"start\">Štart</string>\n    <string name=\"end\">Koniec</string>\n    <string name=\"bilateral_blur\">Bilaterálne rozmazanie</string>\n    <string name=\"vignette\">Vigneta</string>\n    <string name=\"box_blur\">Boxové rozmazanie</string>\n    <string name=\"sepia\">Sépia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Resetovať</string>\n    <string name=\"crop\">Orezať</string>\n    <string name=\"normal\">Normálne</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d zobrazenie</item>\n        <item quantity=\"few\">%d zobrazenia</item>\n        <item quantity=\"many\">%d zobrazení</item>\n        <item quantity=\"other\">%d zobrazení</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%sspríbeh</item>\n        <item quantity=\"few\">%s príbehy</item>\n        <item quantity=\"many\">%s príbehu</item>\n        <item quantity=\"other\">%s príbehov</item>\n    </plurals>\n    <string name=\"details\">Detaily</string>\n    <string name=\"title\">Nadpis</string>\n    <string name=\"members\">Členovia</string>\n    <string name=\"admin\">Administrátor</string>\n    <string name=\"inviter\">Hostiteľ</string>\n    <string name=\"mute_messages\">Stlmiť správy</string>\n    <string name=\"mute_mentions\">Stlmiť zmienenia</string>\n    <string name=\"add_members\">Pridať členov</string>\n    <string name=\"search\">Vyhľadávanie</string>\n    <string name=\"done\">Hotovo</string>\n    <string name=\"dms_action_make_admin\">Vytvoriť sprácu</string>\n    <string name=\"dms_action_remove_admin\">Odstrániť správcu</string>\n    <string name=\"edit_unsuccessful\">Úprava nebola úspešná</string>\n    <string name=\"message\">Správa</string>\n    <string name=\"tap_to_remove\">Kliknutím vymažte</string>\n    <string name=\"forward\">Preposlať</string>\n    <string name=\"forward_outgoing\">You forwarded a message</string>\n    <string name=\"forward_incoming\">Forwarded a message</string>\n    <string name=\"add\">Pridať</string>\n    <string name=\"send\">Odoslať</string>\n    <string name=\"replying_to_yourself\">Replying to yourself</string>\n    <string name=\"replying_to_user\">Replying to %s</string>\n    <string name=\"replied_to_yourself\">You replied to yourself</string>\n    <string name=\"replied_you\">You replied</string>\n    <string name=\"replied_you_group\">You replied to %s</string>\n    <string name=\"replied_group\">Replied to %s</string>\n    <string name=\"replied_to_you\">Replied to you</string>\n    <string name=\"replied_to_themself\">Replied to themself</string>\n    <string name=\"reacted_story_outgoing\">Reagovali ste na ich príbeh</string>\n    <string name=\"reacted_story_incoming\">Reagoval/a na tvoj príbeh</string>\n    <string name=\"mentioned_story_outgoing\">Spomenuli ste ich vo vašom príbehu</string>\n    <string name=\"mentioned_story_incoming\">Spomenul/a ťa v príbehu</string>\n    <string name=\"replied_story_outgoing\">Odpovedal/a si na ich príbeh</string>\n    <string name=\"replied_story_incoming\">Replied to your story</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Fotografia</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Hlasová správa</string>\n    <string name=\"post\">Príspevok</string>\n    <string name=\"approval_required_for_new_members\">Approval required to join</string>\n    <string name=\"requests\">Requests</string>\n    <string name=\"admins_only\">Iba administrátor</string>\n    <string name=\"added_by\">Pridal/a %s</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">Ukončiť chat</string>\n    <string name=\"dms_action_end_question\">Ukončiť chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Odmietnuť</string>\n    <string name=\"accept\">Prijať</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Oznámenia</string>\n    <string name=\"pref_category_post\">Príspevok</string>\n    <string name=\"enable_dm_notifications\">Zapnúť DM oznámenia</string>\n    <string name=\"enable_dm_auto_refesh\">Auto obnovenie správ</string>\n    <string name=\"auto_refresh_every\">Automatické obnovenie každých</string>\n    <string name=\"secs\">sekúnd</string>\n    <string name=\"mins\">minút</string>\n    <string name=\"search_giphy\">Hľadať GIPHY</string>\n    <string name=\"generic_null_response\">Odpoveď je null!</string>\n    <string name=\"generic_not_ok_response\">Stav odpovede nie je ok!</string>\n    <string name=\"generic_failed_request\">Požiadavka zlyhala!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sv/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Systemstandard</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Auto / Följ system</item>\n        <item>Auto / Följ batteri</item>\n        <item>Mörk</item>\n        <item>Ljus</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram-standard (Olästa, sedan lästa)</item>\n        <item>Från nyaste till äldsta</item>\n        <item>Från äldsta till nyaste</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Ingen</item>\n        <item>\\@</item>\n        <item>vid</item>\n        <item>den</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>sekunder</item>\n        <item>minuter</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Om</string>\n    <string name=\"action_dms\">Direktmeddelanden</string>\n    <string name=\"action_settings\">Inställningar</string>\n    <string name=\"action_download\">Ladda ner</string>\n    <string name=\"action_search\">Sök efter användarnamn…</string>\n    <string name=\"action_compare\">Jämför</string>\n    <string name=\"clipboard_error\">Fel uppstod när texten kopierades</string>\n    <string name=\"clipboard_copied\">Kopierades till klippbordet!</string>\n    <string name=\"report\">Rapportera</string>\n    <string name=\"set_password\">Skydda fil med lösenord</string>\n    <string name=\"password_no_max\">Lösenord</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Ja</string>\n    <string name=\"cancel\">Avbryt</string>\n    <string name=\"no\">Nej</string>\n    <string name=\"confirm\">Bekräfta</string>\n    <string name=\"title_favorites\">Favoriter</string>\n    <string name=\"title_discover\">Upptäck</string>\n    <string name=\"title_comments\">Kommentarer</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Aktivitet</string>\n    <string name=\"update_check\">Sök efter uppdatering vid uppstart</string>\n    <string name=\"flag_secure\">Blockera skärmdumpar &amp; förhandsgranskning av appen</string>\n    <string name=\"download_user_folder\">Ladda ner inläggen till användarnamnets mappar</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Markera historier som sedda efter visning</string>\n    <string name=\"mark_as_seen_setting_summary\">Berättelsens författare kommer att veta att du har sett den</string>\n    <string name=\"hide_muted_reels_setting\">Göm historier utan ljud från flödet</string>\n    <string name=\"dm_mark_as_seen_setting\">Markera DM som sedd efter visning</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Andra medlemmar kommer att veta att du har sett den</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Aktivera aktivitetsaviseringar</string>\n    <string name=\"story_sort_setting\">Sortering av historier i flödet</string>\n    <string name=\"error_loading_profile\">Fel uppstod när profilen laddades! Är användarnamnet giltigt? Om det är det så kan du vara data-begränsad.</string>\n    <string name=\"error_loading_hashtag\">Fel uppstod när hashtaggen laddades! Är namnet giltigt?</string>\n    <string name=\"error_loading_location\">Fel uppstod när platsen laddades! Är URL:en giltig?</string>\n    <string name=\"error_creating_folders\">Fel inträffade när mapp(arna) för nerladdning skapades.</string>\n    <string name=\"select_folder\">Välj mapp</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Språk</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nPost</item>\n        <item quantity=\"other\">%s\\nInläggen</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Post</item>\n        <item quantity=\"other\">%s Posts</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nFollower</item>\n        <item quantity=\"other\">%s\\nFollowers</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nFöljer</string>\n    <string name=\"post_viewer_autoplay_video\">Spela upp videor automatiskt</string>\n    <string name=\"post_viewer_background_play\">Fortsätt spela videorna i bakgrunden</string>\n    <string name=\"post_viewer_background_play_summary\">Pausa inte videorna när appen inte längre är i fokus</string>\n    <string name=\"post_viewer_muted_autoplay\">Stäng alltid av ljudet i videorna</string>\n    <string name=\"post_viewer_show_captions\">Always show post captions</string>\n    <string name=\"post_viewer_download_dialog_title\">Välj det som ska laddas ner</string>\n    <string name=\"post_viewer_download_current\">Nuvarande</string>\n    <string name=\"post_viewer_download_album\">Hela albumet</string>\n    <string name=\"show_stories\">Visa historier</string>\n    <string name=\"no_more_stories\">Inga mer historier!</string>\n    <string name=\"view_post\">Visa inlägg</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Answer successful!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d response averaging %s</item>\n        <item quantity=\"other\">%d responses averaging %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Ditt svar: %s</string>\n    <string name=\"reply_story\">Svara till historien</string>\n    <string name=\"reply_hint\">Svara…</string>\n    <string name=\"story_quiz\">Frågesport</string>\n    <string name=\"story_slider\">Slider</string>\n    <string name=\"story_quizzed\">Du har redan svarat!</string>\n    <string name=\"story_mentions\">Omnämnanden</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Det här kontot är Privat</string>\n    <string name=\"priv_acc_confirm\">You won\\'t be able to access posts after unfollowing! Are you sure?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">You can log in via More -&gt; Account on the bottom-right corner or you can view public accounts without login!</string>\n    <string name=\"empty_acc\">Det här kontot har inga inlägg</string>\n    <string name=\"empty_list\">Inga sådana inlägg!</string>\n    <string name=\"login\">Logga in</string>\n    <string name=\"logout\">Logga ut</string>\n    <string name=\"logout_summary\">Browse Instagram anonymously</string>\n    <string name=\"remove_all_acc\">Ta bort alla konton</string>\n    <string name=\"remove_all_acc_warning\">This will remove all added accounts from the app!\\nTo remove just one account, long tap the account from the account switcher dialog.\\nDo you want to continue?</string>\n    <string name=\"time_settings\">Datumformat</string>\n    <string name=\"saved_create_collection\">Skapa ny samling</string>\n    <string name=\"edit_collection\">Redigera samlingens namn</string>\n    <string name=\"delete_collection\">Ta bort samling</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Ta bort från samling</string>\n    <string name=\"liked\">Gillade</string>\n    <string name=\"saved\">Sparade</string>\n    <string name=\"tagged\">Taggade</string>\n    <string name=\"dm_person\">Meddelande</string>\n    <string name=\"follow\">Följ</string>\n    <string name=\"unfollow\">Sluta följa</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorit</string>\n    <string name=\"block\">Blockera</string>\n    <string name=\"unblock\">Ta bort blockering</string>\n    <string name=\"restrict\">Begränsa</string>\n    <string name=\"unrestrict\">Ta bort begränsning</string>\n    <string name=\"mute_stories\">Tysta ner historier</string>\n    <string name=\"mute_posts\">Tysta ner inlägg</string>\n    <string name=\"unmute_stories\">Unmute stories</string>\n    <string name=\"unmute_posts\">Unmute posts</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Kopiera bio</string>\n    <string name=\"bio_translate\">Översätt bio</string>\n    <string name=\"status_mutual\">Ömsesidig</string>\n    <string name=\"status_following\">Följer</string>\n    <string name=\"status_follower\">Följare</string>\n    <string name=\"map\">Karta</string>\n    <string name=\"dialog_export_accounts\">Konton</string>\n    <string name=\"dialog_export_settings\">Inställningar</string>\n    <string name=\"dialog_export_favorites\">Favoriter</string>\n    <string name=\"dialog_import_success\">Importeringen lyckades!</string>\n    <string name=\"dialog_import_failed\">Importeringen misslyckades!</string>\n    <string name=\"dialog_export_success\">Exportering lyckades!</string>\n    <string name=\"dialog_export_failed\">Misslyckades med exportering!</string>\n    <string name=\"refresh\">Uppdatera</string>\n    <string name=\"get_cookies\">Hämta kakor</string>\n    <string name=\"time_settings_title_custom\">Använd anpassat format</string>\n    <string name=\"time_settings_title_separator\">Avskiljare</string>\n    <string name=\"time_settings_title_time_format\">Tidsformat</string>\n    <string name=\"time_settings_title_date_format\">Datumformat</string>\n    <string name=\"time_settings_title_preview\">Förhandsgranska</string>\n    <string name=\"time_settings_swap_time\">Swap Time and Date positions</string>\n    <string name=\"quick_access_cannot_delete_curr\">Cannot delete currently in use account</string>\n    <string name=\"quick_access_confirm_delete\">Är du säker på att du vill ta bort \\'%s\\'?</string>\n    <string name=\"open_profile\">Öppna profil</string>\n    <string name=\"view_story\">View story</string>\n    <string name=\"view_pfp\">View profile picture</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Unsupported message type</string>\n    <string name=\"dms_inbox_unsend\">Unsend message</string>\n    <string name=\"dms_inbox_giphy\">View on GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s shared a post by @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s shared an image</string>\n    <string name=\"dms_inbox_shared_video\">%s shared a video</string>\n    <string name=\"dms_inbox_shared_message\">%s sent a message</string>\n    <string name=\"dms_inbox_shared_gif\">%s shared a gif</string>\n    <string name=\"dms_inbox_shared_sticker\">%s shared a sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s shared a profile: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s shared a location: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s shared a story highlight by @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s shared a story by @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s sent a voice message</string>\n    <string name=\"dms_inbox_shared_clip\">%s shared a clip by @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s shared an IGTV video by @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">You replied to their story: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s replied to your story: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">You reacted to their story: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s reacted to your story: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">You mentioned @%s in your story</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s mentioned you in their story</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Unknown media type</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Media expired!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Delivered</string>\n    <string name=\"dms_inbox_raven_media_sent\">Sent</string>\n    <string name=\"dms_inbox_raven_media_opened\">Opened</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Replayed</string>\n    <string name=\"dms_inbox_raven_media_sending\">Sending…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Blocked</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Suggested</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Screenshotted</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Kan inte leverera</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Tryck och håll in för att spela in ljud</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Lämna chatt</string>\n    <string name=\"dms_action_leave_question\">Lämna den här chatten?</string>\n    <string name=\"dms_action_kick\">Sparka</string>\n    <string name=\"dms_left_users\">Left users</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Ogiltig användare</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram tillåter inte uppladdning av videor som är längre än 60 sekunder för DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram tillåter inte uppladdning av ljud som är längre än 60 sekunder.</string>\n    <string name=\"direct_download_loading\">Hämtar inlägg(en)</string>\n    <string name=\"downloader_complete\">Nerladdningen är färdig</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Laddar ner inlägg…</string>\n    <string name=\"downloader_downloading_media\">Laddar ner media</string>\n    <string name=\"downloader_unknown_error\">Okänt fel inträffade!!!</string>\n    <string name=\"downloader_error_creating_folder\">Error creating folder!</string>\n    <string name=\"downloader_error_download_file\">Error downloading file</string>\n    <string name=\"comment_viewer_translate_comment\">Översätt kommentaren</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Följare</string>\n    <string name=\"followers_type_following\">Följer</string>\n    <string name=\"followers_compare\">Jämför följare &amp; följer</string>\n    <string name=\"followers_both_following\">Båda två följer varandra</string>\n    <string name=\"followers_not_following\">följer inte %s</string>\n    <string name=\"followers_not_follower\">%s följer inte</string>\n    <string name=\"login_error_loading_cookies\">Fel inträffade när kakorna laddades</string>\n    <string name=\"comment_hint\">Skriv en ny kommentar…</string>\n    <string name=\"liked_notif\">Gillade ditt inlägg</string>\n    <string name=\"comment_notif\">Kommenterade ditt inlägg:</string>\n    <string name=\"follow_notif\">Började att följa dig</string>\n    <string name=\"tagged_notif\">Taggade dig i ett inlägg</string>\n    <string name=\"request_notif\">Begärde att följa dig</string>\n    <string name=\"request_approve\">Godkänn begäran</string>\n    <string name=\"request_reject\">Neka begäran</string>\n    <string name=\"share_public_post\">Dela det här publika inlägget till…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">Den här kategorin är på något sätt tom…</string>\n    <string name=\"update_available\">En uppdatering är tillgänglig! (%s)</string>\n    <string name=\"updated\">Tack för att du uppdaterar Barinsta!</string>\n    <string name=\"crash_title\">Appen kraschade</string>\n    <string name=\"crash_descr\">Hoppsan.. appen kraschade, men oroa dig inte. Du kan skicka felrapport till utvecklaren för att hjälpa honom att lösa problemet. (:</string>\n    <string name=\"action_notif\">Aktivitet</string>\n    <string name=\"action_archive\">Story archive</string>\n    <string name=\"action_ayml\">Suggested users</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d följer</string>\n    <string name=\"activity_count_comments\">%d kommentarer</string>\n    <string name=\"activity_count_commentlikes\">%d gillade kommentarer</string>\n    <string name=\"activity_count_usertags\">%d usertags</string>\n    <string name=\"activity_count_likes\">%d gillanden</string>\n    <string name=\"activity_count_poy\">%d bilder på dig</string>\n    <string name=\"activity_count_requests\">%d follow requests</string>\n    <string name=\"activity_notloggedin\">You logged out before clicking this notification?!</string>\n    <string name=\"feed\">Flöde</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Mer</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d valdes</string>\n    <string name=\"logout_success\">Utloggningen lyckades!</string>\n    <string name=\"dm_thread_info\">Info</string>\n    <string name=\"mark_as_seen\">Markera som sedd</string>\n    <string name=\"version\">Version</string>\n    <string name=\"pref_start_screen\">Startskärm</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">Generellt</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">Nerladdningar</string>\n    <string name=\"pref_category_locale\">Locale</string>\n    <string name=\"account\">Konto</string>\n    <string name=\"account_hint\">Current login not working? Simply add the account again.</string>\n    <string name=\"add_account\">Lägg till konto</string>\n    <string name=\"about_category_license\">Licens (Endast på Engelska)</string>\n    <string name=\"about_documentation\">Besök vår hemsida</string>\n    <string name=\"about_documentation_summary\">Get support, discuss, meet others, and have fun!</string>\n    <string name=\"about_repository\">See our source code on GitHub</string>\n    <string name=\"about_repository_summary\">Audit, star, report bugs, contribute, and have fun (again)!</string>\n    <string name=\"about_feedback\">Send feedback by email</string>\n    <string name=\"about_category_3pt\">Third-Party Attributions</string>\n    <string name=\"reminder\">Reminder</string>\n    <string name=\"reminder_summary\">Please use this app responsibly. Downloaded images should only be used for purposes allowed by applicable laws.</string>\n    <string name=\"light_white_theme\">White</string>\n    <string name=\"dark_black_theme\">Black</string>\n    <string name=\"light_theme_settings\">Light theme</string>\n    <string name=\"dark_theme_settings\">Dark theme</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Dark</string>\n    <string name=\"added_to_favs\">Lades till i Favoriter!</string>\n    <string name=\"add_to_favorites\">Lades till i Favoriter</string>\n    <string name=\"accounts\">Konton</string>\n    <string name=\"hashtags\">Hash-taggar</string>\n    <string name=\"locations\">Platser</string>\n    <string name=\"unknown\">Okänd</string>\n    <string name=\"removed_from_favs\">Togs bort från Favoriter!</string>\n    <string name=\"backup_and_restore\">Säkerhetskopiera &amp; Återställ</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration.</string>\n    <string name=\"backup_warning\">If you\\'re backing up account login data, treat the file as confidential and keep it somewhere safe!</string>\n    <string name=\"create_backup\">Create new backup file</string>\n    <string name=\"restore_backup\">Restore from existing backup file</string>\n    <string name=\"file_chosen_label\">Fil:</string>\n    <string name=\"enter_password\">Fyll i lösenord</string>\n    <string name=\"select_backup_file\">Välj en säkerhetskopieringsfil (.zaai/.backup)</string>\n    <string name=\"apply\">Verkställ</string>\n    <string name=\"save\">Spara</string>\n    <string name=\"caption\">Bildtext</string>\n    <string name=\"edit_caption\">Redigera bildtext</string>\n    <string name=\"translate_caption\">Översätt bildtext</string>\n    <string name=\"player_timeline_desc\">Tidslinje för videospelare</string>\n    <string name=\"liking\">Gillar…</string>\n    <string name=\"like_unsuccessful\">Like unsuccessful</string>\n    <string name=\"unlike_unsuccessful\">Unlike unsuccessful</string>\n    <string name=\"unliking\">Unliking…</string>\n    <string name=\"controls\">Kontroller</string>\n    <string name=\"saving\">Sparar…</string>\n    <string name=\"removing\">Tar bort…</string>\n    <string name=\"save_unsuccessful\">Save unsuccessful</string>\n    <string name=\"save_remove_unsuccessful\">Remove unsuccessful</string>\n    <string name=\"downloading\">Laddar ner…</string>\n    <string name=\"downloader_downloading_child\">Ladda ner objekt %1$d av %2$d</string>\n    <string name=\"delete\">Ta bort</string>\n    <string name=\"comment\">Kommentera</string>\n    <string name=\"layout\">Layout</string>\n    <string name=\"feed_stories\">Feed stories</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Dela</string>\n    <string name=\"layout_style\">Layout style</string>\n    <string name=\"column_count\">Column count</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Visa namn</string>\n    <string name=\"show_avatars\">Visa avatarer</string>\n    <string name=\"avatar_size\">Avatarens storlek</string>\n    <string name=\"corners\">Hörnen</string>\n    <string name=\"show_grid_gap\">Show grid gap</string>\n    <string name=\"post_not_found\">Inlägget hittades inte!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Gallery</string>\n    <string name=\"camera\">Camera</string>\n    <string name=\"all_photos\">All Photos</string>\n    <string name=\"all_media\">All Media</string>\n    <string name=\"all_videos\">All Videos</string>\n    <string name=\"brightness\">Brightness</string>\n    <string name=\"contrast\">Contrast</string>\n    <string name=\"vibrance\">Vibrance</string>\n    <string name=\"saturation\">Saturation</string>\n    <string name=\"sharpen\">Sharpen</string>\n    <string name=\"exposure\">Exponering</string>\n    <string name=\"center\">Center</string>\n    <string name=\"color\">Färg</string>\n    <string name=\"start\">Start</string>\n    <string name=\"end\">Slut</string>\n    <string name=\"bilateral_blur\">Bilateral oskärpa</string>\n    <string name=\"vignette\">Vignette</string>\n    <string name=\"box_blur\">Box blur</string>\n    <string name=\"sepia\">Sepia</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Återställ</string>\n    <string name=\"crop\">Beskär</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d view</item>\n        <item quantity=\"other\">%d views</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s story</item>\n        <item quantity=\"other\">%s stories</item>\n    </plurals>\n    <string name=\"details\">Detaljer</string>\n    <string name=\"title\">Titel</string>\n    <string name=\"members\">Medlemmar</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Inviter</string>\n    <string name=\"mute_messages\">Mute messages</string>\n    <string name=\"mute_mentions\">Mute mentions</string>\n    <string name=\"add_members\">Lägg till meddlemmar</string>\n    <string name=\"search\">Sök</string>\n    <string name=\"done\">Färdig</string>\n    <string name=\"dms_action_make_admin\">Gör till admin</string>\n    <string name=\"dms_action_remove_admin\">Ta bort som admin</string>\n    <string name=\"edit_unsuccessful\">Redigeringen lyckades inte</string>\n    <string name=\"message\">Meddelande</string>\n    <string name=\"tap_to_remove\">Tryck för att ta bort</string>\n    <string name=\"forward\">Vidarebefordra</string>\n    <string name=\"forward_outgoing\">Du vidarebefordrade ett meddelande</string>\n    <string name=\"forward_incoming\">Vidarebefordrade ett meddelande</string>\n    <string name=\"add\">Lägg till</string>\n    <string name=\"send\">Skicka</string>\n    <string name=\"replying_to_yourself\">Svarar till dig själv</string>\n    <string name=\"replying_to_user\">Svarar till %s</string>\n    <string name=\"replied_to_yourself\">Du svarade till dig själv</string>\n    <string name=\"replied_you\">Du svarade</string>\n    <string name=\"replied_you_group\">Du svarade till %s</string>\n    <string name=\"replied_group\">Svarade till %s</string>\n    <string name=\"replied_to_you\">Svarade till dig</string>\n    <string name=\"replied_to_themself\">Svarade till sig själva</string>\n    <string name=\"reacted_story_outgoing\">Du reagerade på deras historia</string>\n    <string name=\"reacted_story_incoming\">Reagerade på din historia</string>\n    <string name=\"mentioned_story_outgoing\">Du nämnde dem i din historia</string>\n    <string name=\"mentioned_story_incoming\">Nämnde dig i deras historia</string>\n    <string name=\"replied_story_outgoing\">Du svarade på deras historia</string>\n    <string name=\"replied_story_incoming\">Svarade på deras historia</string>\n    <string name=\"raven_image_expired\">Image has expired</string>\n    <string name=\"raven_image_info\">Image will expire when seen</string>\n    <string name=\"raven_video_expired\">Video has expired</string>\n    <string name=\"raven_video_info\">Video will expire when seen</string>\n    <string name=\"raven_msg_expired\">Message has expired</string>\n    <string name=\"raven_msg_info\">Message will expire when seen</string>\n    <string name=\"story_share\">\\@%s\\'s story</string>\n    <string name=\"story_share_highlight\">\\@%s\\'s story highlight</string>\n    <string name=\"photo\">Foto</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Röstmeddelande</string>\n    <string name=\"post\">Post</string>\n    <string name=\"approval_required_for_new_members\">Approval required to join</string>\n    <string name=\"requests\">Förfrågningar</string>\n    <string name=\"admins_only\">Endast administratörer</string>\n    <string name=\"added_by\">Lades till av %s</string>\n    <string name=\"admin_approval_required\">Admin approval required</string>\n    <string name=\"admin_approval_required_description\">An admin approval will be required to add new members to the group</string>\n    <string name=\"dms_action_end\">End chat</string>\n    <string name=\"dms_action_end_question\">End chat?</string>\n    <string name=\"dms_action_end_description\">All members will be removed from the group. They will still be able to view the chat history.</string>\n    <string name=\"pending_requests\">Pending Requests</string>\n    <string name=\"accept_request_from_user\">Accept request from %1s (%2s)?</string>\n    <string name=\"decline\">Decline</string>\n    <string name=\"accept\">Accept</string>\n    <string name=\"you\">You</string>\n    <string name=\"no_pending_requests\">No pending requests</string>\n    <string name=\"checking_for_new_messages\">Checking for new messages</string>\n    <string name=\"pref_category_stories\">Stories</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Notifications</string>\n    <string name=\"pref_category_post\">Post</string>\n    <string name=\"enable_dm_notifications\">Enable DM notifications</string>\n    <string name=\"enable_dm_auto_refesh\">Auto refresh messages</string>\n    <string name=\"auto_refresh_every\">Auto refresh every</string>\n    <string name=\"secs\">secs</string>\n    <string name=\"mins\">mins</string>\n    <string name=\"search_giphy\">Search GIPHY</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Nyckelord</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Markerad som sedd</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Fel</string>\n    <string name=\"account_logged_out\">Det här kontot har blivit utloggat.</string>\n    <string name=\"login_required\">Inloggning krävs!</string>\n    <string name=\"inactive_user\">Användaren är inaktiv!</string>\n    <string name=\"crash_report_subject\">Kraschrapport för Barinsta</string>\n    <string name=\"crash_report_title\">Välj en e-postapplikation för att skicka kraschloggar</string>\n    <string name=\"not_found\">Hittades inte!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Hoppa över den här uppdateringen</string>\n    <string name=\"on_latest_version\">Du har redan den senaste versionen</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Andra flikar</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Topp</string>\n    <string name=\"recent\">Senaste</string>\n    <string name=\"clear\">Rensa bort</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Sistem Varsayılanı</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Otomatik / Sistemi Takip Et</item>\n        <item>Otomatik / Bataryayı Takip Et</item>\n        <item>Koyu</item>\n        <item>Açık</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram varsayılanı (önce görülmemişler sonra görülmüşler)</item>\n        <item>Yeniden eskiye</item>\n        <item>Eskiden yeniye</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Hiçbiri</item>\n        <item>\\@</item>\n        <item>at</item>\n        <item>on</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>saniye</item>\n        <item>dakika</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Hakkında</string>\n    <string name=\"action_dms\">Direkt Mesajlar</string>\n    <string name=\"action_settings\">Ayarlar</string>\n    <string name=\"action_download\">İndir</string>\n    <string name=\"action_search\">Kullanıcı adı ara…</string>\n    <string name=\"action_compare\">Karşılaştır</string>\n    <string name=\"clipboard_error\">Metin kopyalanamadı</string>\n    <string name=\"clipboard_copied\">Panoya kopyalandı!</string>\n    <string name=\"report\">Bildir</string>\n    <string name=\"set_password\">Dosyayı parola ile koru</string>\n    <string name=\"password_no_max\">Şifre</string>\n    <string name=\"ok\">Tamam</string>\n    <string name=\"yes\">Evet</string>\n    <string name=\"cancel\">İptal</string>\n    <string name=\"no\">Hayır</string>\n    <string name=\"confirm\">Onayla</string>\n    <string name=\"title_favorites\">Favoriler</string>\n    <string name=\"title_discover\">Keşfet</string>\n    <string name=\"title_comments\">Yorumlar</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Hareketler</string>\n    <string name=\"update_check\">Güncellemeleri başlangıçta kontrol et</string>\n    <string name=\"flag_secure\">Block screenshots &amp; app preview</string>\n    <string name=\"download_user_folder\">İndirmeleri kullanıcı adından oluşan bir alt klasörün içine yap</string>\n    <string name=\"download_prepend_username\">Prepend Username to Filename</string>\n    <string name=\"mark_as_seen_setting\">Hikayeleri gördükten sonra görüldü olarak işaretle</string>\n    <string name=\"mark_as_seen_setting_summary\">Hikayeyi paylaşan gördüğünüzü bilecek</string>\n    <string name=\"hide_muted_reels_setting\">Hide muted stories from feed</string>\n    <string name=\"dm_mark_as_seen_setting\">DM\\'leri gördükten sonra görüldü olarak işaretle</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Diğerleri gördüğünüzü bilecek</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Hareketler için bildirimleri aktifleştir</string>\n    <string name=\"story_sort_setting\">Hikaye akışını sınıflandır</string>\n    <string name=\"error_loading_profile\">Profil yükleme hatası! Kullanıcı adında hata var mı? Yoksa hesabınız sınırlandırılmış olabilir.</string>\n    <string name=\"error_loading_hashtag\">Etiketi yüklerken hata oluştu! İsmi doğru mu?</string>\n    <string name=\"error_loading_location\">Profili yüklerken hata oluştu! URL doğru mu?</string>\n    <string name=\"error_creating_folders\">İndirilenler klasörü(leri) oluşturulamadı.</string>\n    <string name=\"select_folder\">Klasörü seçin</string>\n    <string name=\"theme_settings\">Tema</string>\n    <string name=\"select_language\">Dil</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"one\">%s\\nGönderi</item>\n        <item quantity=\"other\">%s\\nGönderi</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"one\">%s Gönderi</item>\n        <item quantity=\"other\">%s Gönderi</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"one\">%s\\nTakipçi</item>\n        <item quantity=\"other\">%s\\nTakipçi</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nTakip</string>\n    <string name=\"post_viewer_autoplay_video\">Videoları otomatik oynat</string>\n    <string name=\"post_viewer_background_play\">Continue videos in background</string>\n    <string name=\"post_viewer_background_play_summary\">Do not pause videos when the app is out of focus</string>\n    <string name=\"post_viewer_muted_autoplay\">Videoları her zaman sustur</string>\n    <string name=\"post_viewer_show_captions\">Gönderi başlıklarını her zaman göster</string>\n    <string name=\"post_viewer_download_dialog_title\">İndirmek için seçin</string>\n    <string name=\"post_viewer_download_current\">Güncel</string>\n    <string name=\"post_viewer_download_album\">Tüm Albüm</string>\n    <string name=\"show_stories\">Hikayeleri göster</string>\n    <string name=\"no_more_stories\">Tüm hikayeler bitti!</string>\n    <string name=\"view_post\">Gönderiyi Gör</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Cevaplama başarılı!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"one\">%d yanıt ortalaması %s</item>\n        <item quantity=\"other\">%d yanıt ortalaması %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Cevabın: %s</string>\n    <string name=\"reply_story\">Hikayeye cevap ver</string>\n    <string name=\"reply_hint\">Yanıtla…</string>\n    <string name=\"story_quiz\">Test</string>\n    <string name=\"story_slider\">Kaydırıcı</string>\n    <string name=\"story_quizzed\">Zaten cevapladınız!</string>\n    <string name=\"story_mentions\">Etiketlenenler</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Bu Hesap Gizlidir</string>\n    <string name=\"priv_acc_confirm\">Takipten çıkarsan gönderilere erişemeyeceksin! Emin misin?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Hesabınıza giriş yapmak için sağ alttan Daha Fazla -&gt; Hesap yolunu takip edebilir ya da giriş yapmadan herkese açık profilleri dolaşabilirsin!</string>\n    <string name=\"empty_acc\">Bu Hesabın Gönderisi Yok</string>\n    <string name=\"empty_list\">Öyle Bir Gönderi Yok!</string>\n    <string name=\"login\">Giriş Yap</string>\n    <string name=\"logout\">Çıkış Yap</string>\n    <string name=\"logout_summary\">Instagram\\'ı anonim olarak dolaş</string>\n    <string name=\"remove_all_acc\">Tüm hesapları kaldır</string>\n    <string name=\"remove_all_acc_warning\">Bu eklenmiş tüm hesapları uygulamadan kaldıracak!\\nSadece bir hesabı kaldırmak için hesap değişimi menüsünden istediğin hesaba basılı tut.\\nDevam etmek istiyor musun?</string>\n    <string name=\"time_settings\">Tarih biçimi</string>\n    <string name=\"saved_create_collection\">Yeni koleksiyon oluştur</string>\n    <string name=\"edit_collection\">Koleksiyon ismini düzenle</string>\n    <string name=\"delete_collection\">Koleksiyonu sil</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Koleksiyondan kaldır</string>\n    <string name=\"liked\">Beğenilenler</string>\n    <string name=\"saved\">Kaydedilenler</string>\n    <string name=\"tagged\">Etiketlenilenler</string>\n    <string name=\"dm_person\">Mesaj</string>\n    <string name=\"follow\">Takip Et</string>\n    <string name=\"unfollow\">Takipten Çık</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Favorilere Ekle</string>\n    <string name=\"block\">Engelle</string>\n    <string name=\"unblock\">Engeli kaldır</string>\n    <string name=\"restrict\">Kısıtla</string>\n    <string name=\"unrestrict\">Kısıtlamayı Kaldır</string>\n    <string name=\"mute_stories\">Hikayeleri sustur</string>\n    <string name=\"mute_posts\">Gönderileri sustur</string>\n    <string name=\"unmute_stories\">Hikayelerin sesini aç</string>\n    <string name=\"unmute_posts\">Gönderilerin sesini aç</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Biyoyu kopyala</string>\n    <string name=\"bio_translate\">Biyoyu çevir</string>\n    <string name=\"status_mutual\">Karşılıklı</string>\n    <string name=\"status_following\">Takip Edilen</string>\n    <string name=\"status_follower\">Takipçi</string>\n    <string name=\"map\">Harita</string>\n    <string name=\"dialog_export_accounts\">Hesaplar</string>\n    <string name=\"dialog_export_settings\">Ayarlar</string>\n    <string name=\"dialog_export_favorites\">Favoriler</string>\n    <string name=\"dialog_import_success\">Başarıyla içeri aktarıldı!</string>\n    <string name=\"dialog_import_failed\">İçe aktarılamadı!</string>\n    <string name=\"dialog_export_success\">Başarıyla dışa aktarıldı!</string>\n    <string name=\"dialog_export_failed\">Dışa aktarılamadı!</string>\n    <string name=\"refresh\">Yenile</string>\n    <string name=\"get_cookies\">Çerezleri al</string>\n    <string name=\"time_settings_title_custom\">Özel biçim kullan</string>\n    <string name=\"time_settings_title_separator\">Ayırıcı</string>\n    <string name=\"time_settings_title_time_format\">Zaman Biçimi</string>\n    <string name=\"time_settings_title_date_format\">Tarih Biçimi</string>\n    <string name=\"time_settings_title_preview\">Önizle</string>\n    <string name=\"time_settings_swap_time\">Saat ve Tarih konumlarını değiştir</string>\n    <string name=\"quick_access_cannot_delete_curr\">Şu an kullanımda olan hesap silinemez</string>\n    <string name=\"quick_access_confirm_delete\">Şunu silmek istediğinden emin misin?: \\'%s\\'</string>\n    <string name=\"open_profile\">Profili aç</string>\n    <string name=\"view_story\">Hikayeyi gör</string>\n    <string name=\"view_pfp\">Profil fotoğrafını gör</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Desteklenmeyen mesaj tipi</string>\n    <string name=\"dms_inbox_unsend\">Gönderilen mesajı geri al</string>\n    <string name=\"dms_inbox_giphy\">GIPHY\\'de görüntüle</string>\n    <string name=\"dms_inbox_shared_post\">%s, @%s tarafından bir gönderi paylaştı</string>\n    <string name=\"dms_inbox_shared_image\">%s bir resim paylaştı</string>\n    <string name=\"dms_inbox_shared_video\">%s bir video paylaştı</string>\n    <string name=\"dms_inbox_shared_message\">%s bir mesaj yolladı</string>\n    <string name=\"dms_inbox_shared_gif\">%s bir gif paylaştı</string>\n    <string name=\"dms_inbox_shared_sticker\">%s bir çıkartma paylaştı</string>\n    <string name=\"dms_inbox_shared_profile\">%s bir profil paylaştı: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s bir konum paylaştı: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s, @%s tarafından öne çıkarılan bir hikaye paylaştı</string>\n    <string name=\"dms_inbox_shared_story\">%s, @%s tarafından bir gönderi paylaştı</string>\n    <string name=\"dms_inbox_shared_voice\">%s sesli mesaj gönderdi</string>\n    <string name=\"dms_inbox_shared_clip\">%s, @%s tarafından bir video paylaştı</string>\n    <string name=\"dms_inbox_shared_igtv\">%s, @%s tarafından bir IGTV videosu paylaştı</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Hikayesine yanıt verdin: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s hikayene yanıt verdi: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Hikayesine ifade bıraktın: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s hikayene ifade bıraktı: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">\\@%s kullanıcısını hikayende etiketledin</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s hikayesinde senden bahsetti</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Bilinmeyen medya tipi</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Medyanın zamanı doldu!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">İletildi</string>\n    <string name=\"dms_inbox_raven_media_sent\">Gönderildi</string>\n    <string name=\"dms_inbox_raven_media_opened\">Açıldı</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Tekrar oynatıldı</string>\n    <string name=\"dms_inbox_raven_media_sending\">Gönderiliyor…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Engellendi</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Önerilen</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Ekran görüntüsü alındı</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">İletilemiyor</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Ses kaydetmek için dokun ve basılı tut</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Sohbetten ayrıl</string>\n    <string name=\"dms_action_leave_question\">Bu sohbetten ayrıl?</string>\n    <string name=\"dms_action_kick\">Dışarıya At</string>\n    <string name=\"dms_left_users\">Terk eden kullanıcılar</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Invalid user</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram does not allow uploading videos longer than 60 secs for DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram does not allow uploading audio longer than 60 secs.</string>\n    <string name=\"direct_download_loading\">Gönderi(ler) getiriliyor</string>\n    <string name=\"downloader_complete\">İndirme tamamlandı</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Gönderi indiriliyor…</string>\n    <string name=\"downloader_downloading_media\">Medya indiriliyor</string>\n    <string name=\"downloader_unknown_error\">Bilinmeyen bir hata oluştu!!!</string>\n    <string name=\"downloader_error_creating_folder\">Klasör oluşturma hatası!</string>\n    <string name=\"downloader_error_download_file\">Dosya indirme hatası</string>\n    <string name=\"comment_viewer_translate_comment\">Yorumu çevir</string>\n    <string name=\"comment_viewer_delete_comment\">Delete comment</string>\n    <string name=\"followers_type_followers\">Takipçiler</string>\n    <string name=\"followers_type_following\">Takip Edilen</string>\n    <string name=\"followers_compare\">Takipçiler &amp; takip edilenler karşılaştırması</string>\n    <string name=\"followers_both_following\">Karşılıklı takip edilenler</string>\n    <string name=\"followers_not_following\">takip etmeyen takipçiler %s</string>\n    <string name=\"followers_not_follower\">%s takip edilmeyen takipçi</string>\n    <string name=\"login_error_loading_cookies\">Çerezleri yükleme hatası</string>\n    <string name=\"comment_hint\">Yeni bir yorum yaz…</string>\n    <string name=\"liked_notif\">Gönderini beğendi</string>\n    <string name=\"comment_notif\">Gönderine yorum yaptı:</string>\n    <string name=\"follow_notif\">Seni takip etmeye başladı</string>\n    <string name=\"tagged_notif\">Seni bir gönderide etiketledi</string>\n    <string name=\"request_notif\">Seni takip etmek istiyor</string>\n    <string name=\"request_approve\">İsteği onayla</string>\n    <string name=\"request_reject\">İsteği reddet</string>\n    <string name=\"share_public_post\">Bu genel gönderiyi paylaş…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">Bu kategori nasıl oluyorsa boş…</string>\n    <string name=\"update_available\">Yeni bir güncelleme mevcut! (%s)</string>\n    <string name=\"updated\">Barinsta\\'yı güncellediğin için teşekkürler!</string>\n    <string name=\"crash_title\">Uygulama çöktü</string>\n    <string name=\"crash_descr\">Amanın.. uygulama çöktü, ama telaşlanma. Geliştiriciye hata raporu yollayarak sorunun çözülmesine yardımcı olabilirsin. (:</string>\n    <string name=\"action_notif\">Hareketler</string>\n    <string name=\"action_archive\">Hikaye arşivi</string>\n    <string name=\"action_ayml\">Önerilen kullanıcılar</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"one\">You have %d notification</item>\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d takip</string>\n    <string name=\"activity_count_comments\">%d yorum</string>\n    <string name=\"activity_count_commentlikes\">%d yorum beğenisi</string>\n    <string name=\"activity_count_usertags\">%d kullanıcı etiketi</string>\n    <string name=\"activity_count_likes\">%d beğeni</string>\n    <string name=\"activity_count_poy\">%d tane fotoğrafın</string>\n    <string name=\"activity_count_requests\">%d tane takip isteği</string>\n    <string name=\"activity_notloggedin\">Bu bildirime tıklamadan önce çıkış yaptın?!</string>\n    <string name=\"feed\">Akış</string>\n    <string name=\"profile\">Profil</string>\n    <string name=\"more\">Fazlası</string>\n    <string name=\"title_dm\">Mesajlar</string>\n    <string name=\"number_selected\">%d seçildi</string>\n    <string name=\"logout_success\">Başarıyla çıkış yapıldı!</string>\n    <string name=\"dm_thread_info\">Bilgi</string>\n    <string name=\"mark_as_seen\">Görüldü olarak işaretle</string>\n    <string name=\"version\">Sürüm</string>\n    <string name=\"pref_start_screen\">Başlangıç ekranı</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">Genel</string>\n    <string name=\"pref_category_theme\">Tema</string>\n    <string name=\"pref_category_downloads\">İndirmeler</string>\n    <string name=\"pref_category_locale\">Yerel</string>\n    <string name=\"account\">Hesap</string>\n    <string name=\"account_hint\">Bulunduğunuz hesap çalışmıyor mu? Hesabı tekrar eklemeyi deneyin.</string>\n    <string name=\"add_account\">Hesap ekle</string>\n    <string name=\"about_category_license\">Lisans (İngilizce)</string>\n    <string name=\"about_documentation\">İnternet sitemizi ziyaret et</string>\n    <string name=\"about_documentation_summary\">Destek al, tartışmalara katıl, diğerleriyle tanış ve eğlen!</string>\n    <string name=\"about_repository\">Kaynak kodumuzu Github üzerinde gör</string>\n    <string name=\"about_repository_summary\">Denetle, yıldızla, hataları bildir, katkıda bulun ve keyfini çıkart (tekrar)!</string>\n    <string name=\"about_feedback\">Eposta yoluyla geribildirim gönder</string>\n    <string name=\"about_category_3pt\">Üçüncü-Parti Atıfları</string>\n    <string name=\"reminder\">Hatırlatıcı</string>\n    <string name=\"reminder_summary\">Lütfen bu uygulamayı bilinçli bir şekilde kullan. İndirilen gönderiler, kanunlar çerçevesinde kullanılmalıdır.</string>\n    <string name=\"light_white_theme\">Beyaz</string>\n    <string name=\"dark_black_theme\">Siyah</string>\n    <string name=\"light_theme_settings\">Açık tema</string>\n    <string name=\"dark_theme_settings\">Koyu Tema</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Materyal Koyu</string>\n    <string name=\"added_to_favs\">Favorilere Eklendi!</string>\n    <string name=\"add_to_favorites\">Favorilere Ekle</string>\n    <string name=\"accounts\">Hesaplar</string>\n    <string name=\"hashtags\">Etiketler</string>\n    <string name=\"locations\">Konumlar</string>\n    <string name=\"unknown\">Bilinmeyen</string>\n    <string name=\"removed_from_favs\">Favorilerden Kaldırıldı!</string>\n    <string name=\"backup_and_restore\">Yedekle &amp; Geri Yükle</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Daha sonra geri yüklemek için; uygulama ayarlarını, giriş bilgilerini, ve/veya favori verilerini düz metin belgesi ya da şifrelenmiş yedek dosyası olarak yedekle.</string>\n    <string name=\"backup_warning\">Eğer giriş yapma bilginizi yedekliyorsan, bu dosyanın gizli olması gerektiğini bil ve güvenli bir yerde depola!</string>\n    <string name=\"create_backup\">Yeni yedekleme dosyası oluştur</string>\n    <string name=\"restore_backup\">Var olan yedekleme dosyasından geri yükle</string>\n    <string name=\"file_chosen_label\">Dosya:</string>\n    <string name=\"enter_password\">Şifreyi gir</string>\n    <string name=\"select_backup_file\">Yedekleme dosyası seç (.zaai/.backup)</string>\n    <string name=\"apply\">Uygula</string>\n    <string name=\"save\">Kaydet</string>\n    <string name=\"caption\">Başlık</string>\n    <string name=\"edit_caption\">Başlığı düzenle</string>\n    <string name=\"translate_caption\">Başlığı çevir</string>\n    <string name=\"player_timeline_desc\">Video oynatıcı zaman çizelgesi</string>\n    <string name=\"liking\">Beğeniliyor…</string>\n    <string name=\"like_unsuccessful\">Beğenme başarısız oldu</string>\n    <string name=\"unlike_unsuccessful\">Beğenmekten vazgeçme başarısız oldu</string>\n    <string name=\"unliking\">Beğenmekten vazgeçiliyor…</string>\n    <string name=\"controls\">Kontroller</string>\n    <string name=\"saving\">Kaydediliyor…</string>\n    <string name=\"removing\">Siliniyor…</string>\n    <string name=\"save_unsuccessful\">Kaydetme işlemi başarısız oldu</string>\n    <string name=\"save_remove_unsuccessful\">Silme işlemi başarısız oldu</string>\n    <string name=\"downloading\">İndiriliyor…</string>\n    <string name=\"downloader_downloading_child\">İndiriliyor (%1$d / %2$d)</string>\n    <string name=\"delete\">Sil</string>\n    <string name=\"comment\">Yorum yap</string>\n    <string name=\"layout\">Yerleşim</string>\n    <string name=\"feed_stories\">Hikaye akışı</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Paylaş</string>\n    <string name=\"layout_style\">Yerleşim biçimi</string>\n    <string name=\"column_count\">Sütun sayısı</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">İsimleri göster</string>\n    <string name=\"show_avatars\">Avatarları göster</string>\n    <string name=\"avatar_size\">Avatar boyutu</string>\n    <string name=\"corners\">Köşeler</string>\n    <string name=\"show_grid_gap\">Izgara boşluklarını göster</string>\n    <string name=\"post_not_found\">Gönderi bulunamadı!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Galeri</string>\n    <string name=\"camera\">Kamera</string>\n    <string name=\"all_photos\">Tüm Fotoğraflar</string>\n    <string name=\"all_media\">Tüm Medya</string>\n    <string name=\"all_videos\">Tüm Videolar</string>\n    <string name=\"brightness\">Parlaklık</string>\n    <string name=\"contrast\">Zıtlık</string>\n    <string name=\"vibrance\">Renk Canlandırması</string>\n    <string name=\"saturation\">Renk Doygunluğu</string>\n    <string name=\"sharpen\">Keskinleştir</string>\n    <string name=\"exposure\">Pozlama</string>\n    <string name=\"center\">Merkez</string>\n    <string name=\"color\">Renk</string>\n    <string name=\"start\">Başla</string>\n    <string name=\"end\">Bitir</string>\n    <string name=\"bilateral_blur\">İkili Bulanıklaştırma</string>\n    <string name=\"vignette\">Köşe Karartma</string>\n    <string name=\"box_blur\">Kutu bulanıklaştırma</string>\n    <string name=\"sepia\">Sepya</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Sıfırla</string>\n    <string name=\"crop\">Kırp</string>\n    <string name=\"normal\">Normal</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"one\">%d görüntüleme</item>\n        <item quantity=\"other\">%d görüntüleme</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"one\">%s hikaye</item>\n        <item quantity=\"other\">%s hikaye</item>\n    </plurals>\n    <string name=\"details\">Detaylar</string>\n    <string name=\"title\">Başlık</string>\n    <string name=\"members\">Üyeler</string>\n    <string name=\"admin\">Admin</string>\n    <string name=\"inviter\">Davet edici</string>\n    <string name=\"mute_messages\">Mesajları sustur</string>\n    <string name=\"mute_mentions\">Etiketlemeleri sustur</string>\n    <string name=\"add_members\">Üye ekle</string>\n    <string name=\"search\">Ara</string>\n    <string name=\"done\">Bitti</string>\n    <string name=\"dms_action_make_admin\">Yönetici Yap</string>\n    <string name=\"dms_action_remove_admin\">Yöneticiliği Kaldır</string>\n    <string name=\"edit_unsuccessful\">Düzenleme başarısız oldu</string>\n    <string name=\"message\">Mesaj</string>\n    <string name=\"tap_to_remove\">Silmek için dokun</string>\n    <string name=\"forward\">İlet</string>\n    <string name=\"forward_outgoing\">Bir mesaj ilettin</string>\n    <string name=\"forward_incoming\">Bir mesaj iletildi</string>\n    <string name=\"add\">Ekle</string>\n    <string name=\"send\">Gönder</string>\n    <string name=\"replying_to_yourself\">Kendine cevap veriyorsun</string>\n    <string name=\"replying_to_user\">Yanıtlanıyor: %s</string>\n    <string name=\"replied_to_yourself\">Kendine cevap verdin</string>\n    <string name=\"replied_you\">Cevapladın</string>\n    <string name=\"replied_you_group\">%s kullanıcısına yanıt verdin</string>\n    <string name=\"replied_group\">%s kullanıcısına yanıt verdi</string>\n    <string name=\"replied_to_you\">Sana cevap verdi</string>\n    <string name=\"replied_to_themself\">Kendisine cevap verdi</string>\n    <string name=\"reacted_story_outgoing\">Hikayesine ifade bıraktın</string>\n    <string name=\"reacted_story_incoming\">Hikayene ifade bıraktı</string>\n    <string name=\"mentioned_story_outgoing\">Hikayende etiketledin</string>\n    <string name=\"mentioned_story_incoming\">Hikayesinde senden bahsetti</string>\n    <string name=\"replied_story_outgoing\">Hikayesine yanıt verdin</string>\n    <string name=\"replied_story_incoming\">Hikayene yanıt verdi</string>\n    <string name=\"raven_image_expired\">Resmin süresi doldu</string>\n    <string name=\"raven_image_info\">Resmin süresi görüldüğünde dolacak</string>\n    <string name=\"raven_video_expired\">Videonun süresi doldu</string>\n    <string name=\"raven_video_info\">Videonun süresi görüldüğünde dolacak</string>\n    <string name=\"raven_msg_expired\">Mesajın süresi doldu</string>\n    <string name=\"raven_msg_info\">Mesajın süresi görüldüğünde dolacak</string>\n    <string name=\"story_share\">\\@%s kullanıcısının hikayesi</string>\n    <string name=\"story_share_highlight\">\\@%s kullanıcısının öne çıkan hikayesi</string>\n    <string name=\"photo\">Fotoğraf</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Sesli mesaj</string>\n    <string name=\"post\">Gönderi</string>\n    <string name=\"approval_required_for_new_members\">Katılmak için onay gerekli</string>\n    <string name=\"requests\">İstekler</string>\n    <string name=\"admins_only\">Sadece yöneticiler</string>\n    <string name=\"added_by\">%s tarafından eklendi</string>\n    <string name=\"admin_approval_required\">Yönetici onayı gerekli</string>\n    <string name=\"admin_approval_required_description\">Gruba yeni üyeler eklemek için yönetici onayı gerekecek</string>\n    <string name=\"dms_action_end\">Sohbeti bitir</string>\n    <string name=\"dms_action_end_question\">Sohbeti bitir?</string>\n    <string name=\"dms_action_end_description\">Tüm üyeler gruptan silinecek. Yine de sohbeti görmeye devam edebilecekler.</string>\n    <string name=\"pending_requests\">Bekleyen İstekler</string>\n    <string name=\"accept_request_from_user\">%1s (%2s) tarafından gelen isteği kabul et?</string>\n    <string name=\"decline\">Reddet</string>\n    <string name=\"accept\">Kabul Et</string>\n    <string name=\"you\">Sen</string>\n    <string name=\"no_pending_requests\">Bekleyen istek yok</string>\n    <string name=\"checking_for_new_messages\">Yeni mesajlar kontrol ediliyor</string>\n    <string name=\"pref_category_stories\">Hikayeler</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Bildirimler</string>\n    <string name=\"pref_category_post\">Gönderi</string>\n    <string name=\"enable_dm_notifications\">DM bildirimlerini aktifleştir</string>\n    <string name=\"enable_dm_auto_refesh\">Mesajları otomatik yenile</string>\n    <string name=\"auto_refresh_every\">Otomatik yenileme sıklığı</string>\n    <string name=\"secs\">saniye</string>\n    <string name=\"mins\">dakika</string>\n    <string name=\"search_giphy\">GIPHY\\'de ara</string>\n    <string name=\"generic_null_response\">Response is null!</string>\n    <string name=\"generic_not_ok_response\">Response status is not ok!</string>\n    <string name=\"generic_failed_request\">Request failed!</string>\n    <string name=\"hint_keyword\">Keyword</string>\n    <string name=\"toggle_keyword_filter\">Enable keyword filter</string>\n    <string name=\"edit_keyword_filter\">Edit keyword filters</string>\n    <string name=\"added_keywords\">Added keyword: %s to filter list</string>\n    <string name=\"removed_keywords\">Removed keyword: %s from filter list</string>\n    <string name=\"marked_as_seen\">Marked as seen</string>\n    <string name=\"delete_unsuccessful\">Delete unsuccessful</string>\n    <string name=\"throttle_error\">Throttled by Instagram because of too many API requests. Wait for some time before retrying.</string>\n    <string name=\"error\">Error</string>\n    <string name=\"account_logged_out\">This account has been logged out.</string>\n    <string name=\"login_required\">Login required!</string>\n    <string name=\"inactive_user\">User is inactive!</string>\n    <string name=\"crash_report_subject\">Barinsta Crash Report</string>\n    <string name=\"crash_report_title\">Select an email app to send crash logs</string>\n    <string name=\"not_found\">Not found!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Skip this update</string>\n    <string name=\"on_latest_version\">You\\'re already on the latest version</string>\n    <string name=\"tab_order\">Screen order</string>\n    <string name=\"other_tabs\">Other tabs</string>\n    <string name=\"tab_order_start_next_launch\">The tab order will be reflected on next launch</string>\n    <string name=\"dm_remove_warning\">If saved, all DM related features will be disabled on next launch</string>\n    <string name=\"copy_caption\">Copy caption</string>\n    <string name=\"copy_reply\">Copy reply</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Top</string>\n    <string name=\"recent\">Recent</string>\n    <string name=\"clear\">Clear</string>\n    <string name=\"no_external_map_app\">No Map app found!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-vi/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>Mặc định hệ thống</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>Tự động / Theo hệ thống</item>\n        <item>Tự động / Theo pin</item>\n        <item>Tối</item>\n        <item>Sáng</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Mặc định của Instagram (chưa đọc rồi đọc)</item>\n        <item>Mới nhất đến cũ nhất</item>\n        <item>Cũ nhất đến mới nhất</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>Không</item>\n        <item>\\@</item>\n        <item>tại</item>\n        <item>lúc</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>giây</item>\n        <item>phút</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">Giới thiệu</string>\n    <string name=\"action_dms\">Tin nhắn trực tiếp</string>\n    <string name=\"action_settings\">Cài đặt</string>\n    <string name=\"action_download\">Tải xuống</string>\n    <string name=\"action_search\">Tìm tên người dùng…</string>\n    <string name=\"action_compare\">So sánh</string>\n    <string name=\"clipboard_error\">Lỗi khi sao chép</string>\n    <string name=\"clipboard_copied\">Đã sao chép vào clipboard!</string>\n    <string name=\"report\">Báo cáo</string>\n    <string name=\"set_password\">Bảo vệ file với mật khẩu</string>\n    <string name=\"password_no_max\">Mật khẩu</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"yes\">Có</string>\n    <string name=\"cancel\">Huỷ</string>\n    <string name=\"no\">Không</string>\n    <string name=\"confirm\">Xác nhận</string>\n    <string name=\"title_favorites\">Yêu thích</string>\n    <string name=\"title_discover\">Khám phá</string>\n    <string name=\"title_comments\">Bình luận</string>\n    <string name=\"title_replies\">Replies</string>\n    <string name=\"title_notifications\">Hoạt động</string>\n    <string name=\"update_check\">Kiểm tra cập nhật khi khởi động</string>\n    <string name=\"flag_secure\">Chặn chụp ảnh màn hình &amp; xem trước ứng dụng</string>\n    <string name=\"download_user_folder\">Tải bài viết xuống theo thư mục tên người dùng trong Downloads</string>\n    <string name=\"download_prepend_username\">Thêm tên người dùng vào tên tệp</string>\n    <string name=\"mark_as_seen_setting\">Đánh dấu story là đã xem sau khi xem</string>\n    <string name=\"mark_as_seen_setting_summary\">Người đăng story sẽ biết bạn đã xem nó</string>\n    <string name=\"hide_muted_reels_setting\">Ẩn những story đã chặn khỏi bảng tin</string>\n    <string name=\"dm_mark_as_seen_setting\">Đánh dấu DM là đã xem sau khi xem</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">Những thành viên khác sẽ biết bạn đã xem nó</string>\n    <string name=\"autoplay_stories_setting\">Autoplay video stories</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">Kích hoạt thông báo hoạt động</string>\n    <string name=\"story_sort_setting\">Sắp xếp câu chuyện trên bảng tin</string>\n    <string name=\"error_loading_profile\">Tải hồ sơ thất bại! Tên người dùng có đúng chưa? Nếu đúng, có thể bạn đã bị giới hạn.</string>\n    <string name=\"error_loading_hashtag\">Tải hashtag thất bại! Tên đã đúng chứ?</string>\n    <string name=\"error_loading_location\">Tải vị trí thất bại! URL đã đúng chưa?</string>\n    <string name=\"error_creating_folders\">Lỗi khi tạo thư mục Download.</string>\n    <string name=\"select_folder\">Chọn thư mục</string>\n    <string name=\"theme_settings\">Giao diện</string>\n    <string name=\"select_language\">Ngôn ngữ</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\nBài đăng</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s Bài đăng</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\nNgười theo dõi</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\nĐang theo dõi</string>\n    <string name=\"post_viewer_autoplay_video\">Tự động phát video</string>\n    <string name=\"post_viewer_background_play\">Tiếp tục video trong nền</string>\n    <string name=\"post_viewer_background_play_summary\">Không dừng video khi app trong đa nhiệm</string>\n    <string name=\"post_viewer_muted_autoplay\">Luôn luôn tắt âm thanh video</string>\n    <string name=\"post_viewer_show_captions\">Luôn hiển thị tiêu đề của bài viết</string>\n    <string name=\"post_viewer_download_dialog_title\">Chọn mục tải xuống</string>\n    <string name=\"post_viewer_download_current\">Hiện tại</string>\n    <string name=\"post_viewer_download_album\">Cả album</string>\n    <string name=\"show_stories\">Hiện story</string>\n    <string name=\"no_more_stories\">Không còn story nào!</string>\n    <string name=\"view_post\">Xem Bài đăng</string>\n    <string name=\"story_poll\">Poll</string>\n    <string name=\"answered_story\">Trả lời thành công!</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d Trung bình %s</item>\n    </plurals>\n    <string name=\"slider_answer\">Câu trả lời của bạn: %s</string>\n    <string name=\"reply_story\">Trả lời story</string>\n    <string name=\"reply_hint\">Trả lời…</string>\n    <string name=\"story_quiz\">Câu hỏi</string>\n    <string name=\"story_slider\">Thanh trượt</string>\n    <string name=\"story_quizzed\">Bạn đã trả lời rồi!</string>\n    <string name=\"story_mentions\">Đề cập</string>\n    <string name=\"story_question\">Question</string>\n    <string name=\"priv_acc\">Tài khoản này là riêng tư</string>\n    <string name=\"priv_acc_confirm\">Bạn sẽ không thể truy cập vào bài đăng sau khi bỏ theo dõi? Bạn chắc chứ?</string>\n    <string name=\"are_you_sure\">Are you sure?</string>\n    <string name=\"no_acc\">Bạn có thể đăng nhập bằng Thêm -&gt; Tài khoản ở góc phải dưới hoặc bạn có thể xem tài khoản công khai mà không cần đăng nhập!</string>\n    <string name=\"empty_acc\">Tài khoản này không có bài viết nào</string>\n    <string name=\"empty_list\">Không có bài đăng đó!</string>\n    <string name=\"login\">Đăng nhập</string>\n    <string name=\"logout\">Đăng xuất</string>\n    <string name=\"logout_summary\">Lướt Instagram ẩn danh</string>\n    <string name=\"remove_all_acc\">Xóa tất cả tài khoản</string>\n    <string name=\"remove_all_acc_warning\">Điều này sẽ xóa tất cả tài khoản đã thêm vào ứng dụng!\\nĐể xóa một tài khoản duy nhất, nhấn giữ vào tài khoản đó trong mục đổi tài khoản.\\nBạn có muốn tiếp tục không?</string>\n    <string name=\"time_settings\">Định dạng ngày tháng</string>\n    <string name=\"saved_create_collection\">Tạo bộ sưu tập mới</string>\n    <string name=\"edit_collection\">Chỉnh sửa tên bộ sưu tập</string>\n    <string name=\"delete_collection\">Xoá bộ sưu tập</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">Add to collection…</string>\n    <string name=\"remove_from_collection\">Xoá khỏi bộ sưu tập</string>\n    <string name=\"liked\">Đã thích</string>\n    <string name=\"saved\">Đã lưu</string>\n    <string name=\"tagged\">Đã gắn thẻ</string>\n    <string name=\"dm_person\">Tin nhắn</string>\n    <string name=\"follow\">Theo dõi</string>\n    <string name=\"unfollow\">Bỏ theo dõi</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">Yêu thích</string>\n    <string name=\"block\">Chặn</string>\n    <string name=\"unblock\">Bỏ chặn</string>\n    <string name=\"restrict\">Giới hạn</string>\n    <string name=\"unrestrict\">Bỏ giới hạn</string>\n    <string name=\"mute_stories\">Chặn story</string>\n    <string name=\"mute_posts\">Chặn bài đăng</string>\n    <string name=\"unmute_stories\">Bỏ chặn story</string>\n    <string name=\"unmute_posts\">Bỏ chặn bài đăng</string>\n    <string name=\"remove_follower\">Remove follower</string>\n    <string name=\"bio_copy\">Sao chép thông tin</string>\n    <string name=\"bio_translate\">Dịch thông tin</string>\n    <string name=\"status_mutual\">Chung</string>\n    <string name=\"status_following\">Đang theo dõi</string>\n    <string name=\"status_follower\">Người theo dõi</string>\n    <string name=\"map\">Bản đồ</string>\n    <string name=\"dialog_export_accounts\">Tài khoản</string>\n    <string name=\"dialog_export_settings\">Cài đặt</string>\n    <string name=\"dialog_export_favorites\">Yêu thích</string>\n    <string name=\"dialog_import_success\">Đã nhập thành công!</string>\n    <string name=\"dialog_import_failed\">Nhập dữ liệu thất bại!</string>\n    <string name=\"dialog_export_success\">Đã xuất thành công!</string>\n    <string name=\"dialog_export_failed\">Xuất dữ liệu thất bại!</string>\n    <string name=\"refresh\">Làm mới</string>\n    <string name=\"get_cookies\">Lấy cookies</string>\n    <string name=\"time_settings_title_custom\">Dùng định dạng tuỳ chỉnh</string>\n    <string name=\"time_settings_title_separator\">Phân cách</string>\n    <string name=\"time_settings_title_time_format\">Định dạng Thời gian</string>\n    <string name=\"time_settings_title_date_format\">Định dạng Ngày tháng</string>\n    <string name=\"time_settings_title_preview\">Xem trước</string>\n    <string name=\"time_settings_swap_time\">Đổi vị trí ngày và giờ</string>\n    <string name=\"quick_access_cannot_delete_curr\">Không thể xóa tài khoản đang được sử dụng</string>\n    <string name=\"quick_access_confirm_delete\">Bạn có chắc bạn muốn xóa \\'%s\\'?</string>\n    <string name=\"open_profile\">Mở hồ sơ</string>\n    <string name=\"view_story\">Xem story</string>\n    <string name=\"view_pfp\">Xem Ảnh Đại Diện</string>\n    <string name=\"dms_inbox_raven_message_unknown\">Dạng tin nhắn không được hỗ trợ</string>\n    <string name=\"dms_inbox_unsend\">Gỡ tin nhắn</string>\n    <string name=\"dms_inbox_giphy\">Xem trên GIPHY</string>\n    <string name=\"dms_inbox_shared_post\">%s đã chia sẻ bài viết của @%s</string>\n    <string name=\"dms_inbox_shared_image\">%s đã chia sẻ một bức ảnh</string>\n    <string name=\"dms_inbox_shared_video\">%s đã chia sẻ một video</string>\n    <string name=\"dms_inbox_shared_message\">%s đã gửi một tin nhắn</string>\n    <string name=\"dms_inbox_shared_gif\">%s đã chia sẻ một ảnh động</string>\n    <string name=\"dms_inbox_shared_sticker\">%s đã chia sẻ một sticker</string>\n    <string name=\"dms_inbox_shared_profile\">%s đã chia sẻ một hồ sơ: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s% đã chia sẻ vị trí: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s đã chia sẻ một story highlight của @%s</string>\n    <string name=\"dms_inbox_shared_story\">%s đã chia sẻ một story của @%s</string>\n    <string name=\"dms_inbox_shared_voice\">%s đã gửi một tin nhắn thoại</string>\n    <string name=\"dms_inbox_shared_clip\">%s đã chia sẻ một đoạn clip của @%s</string>\n    <string name=\"dms_inbox_shared_igtv\">%s đã chia sẻ một đoạn video IGTV của @%s</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">Bạn đã phản hồi story của họ: %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s đã phản hồi story của bạn: %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">Bạn đã bày tỏ cảm xúc story của họ: %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s đã bày tỏ cảm xúc story của bạn: %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">Bạn đã gắn thẻ @%s trong story của mình</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s đã nhắc đến bạn trong story của họ</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>Phương tiện không xác định</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">Phương tiện đã hết hạn!</string>\n    <string name=\"dms_inbox_raven_media_delivered\">Đã gửi</string>\n    <string name=\"dms_inbox_raven_media_sent\">Đã gửi</string>\n    <string name=\"dms_inbox_raven_media_opened\">Đã mở</string>\n    <string name=\"dms_inbox_raven_media_replayed\">Đã phát lại</string>\n    <string name=\"dms_inbox_raven_media_sending\">Đang gửi…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">Đã chặn</string>\n    <string name=\"dms_inbox_raven_media_suggested\">Gợi ý</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">Đã chụp màn hình</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">Không thể gửi</string>\n    <string name=\"dms_thread_message_hint\">Message…</string>\n    <string name=\"dms_thread_audio_hint\">Chạm và giữ để ghi âm</string>\n    <string name=\"dms_thread_updating\">Updating…</string>\n    <string name=\"dms_action_leave\">Rời khỏi trò chuyện</string>\n    <string name=\"dms_action_leave_question\">Rời khỏi nhóm này?</string>\n    <string name=\"dms_action_kick\">Đá</string>\n    <string name=\"dms_left_users\">Người dùng đã rời đi</string>\n    <string name=\"dms_ERROR_INVALID_USER\">Người dùng không hợp lệ</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram không cho phép đăng tải video trên 60 giây trong DM.</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram không cho phép đăng tải ghi âm trên 60 giây.</string>\n    <string name=\"direct_download_loading\">Đang nạp bài viết</string>\n    <string name=\"downloader_complete\">Tải xuống hoàn tất</string>\n    <string name=\"downloader_preparing\">Preparing to download…</string>\n    <string name=\"downloader_downloading_post\">Đang tải xuống bài viết…</string>\n    <string name=\"downloader_downloading_media\">Đang tải xuống phương tiện</string>\n    <string name=\"downloader_unknown_error\">Đã xảy ra lỗi không xác định!!!</string>\n    <string name=\"downloader_error_creating_folder\">Lỗi khi tạo thư mục!</string>\n    <string name=\"downloader_error_download_file\">Lỗi khi tải xuống tệp</string>\n    <string name=\"comment_viewer_translate_comment\">Dịch bình luận</string>\n    <string name=\"comment_viewer_delete_comment\">Xóa bình luận</string>\n    <string name=\"followers_type_followers\">Người theo dõi</string>\n    <string name=\"followers_type_following\">Đang theo dõi</string>\n    <string name=\"followers_compare\">So sánh người theo dõi &amp; đang theo dõi</string>\n    <string name=\"followers_both_following\">Đang theo dõi lẫn nhau</string>\n    <string name=\"followers_not_following\">không theo dõi %s</string>\n    <string name=\"followers_not_follower\">%s không theo dõi</string>\n    <string name=\"login_error_loading_cookies\">Lỗi khi tải cookies</string>\n    <string name=\"comment_hint\">Viết bình luận mới…</string>\n    <string name=\"liked_notif\">Đã thích bài đăng của bạn</string>\n    <string name=\"comment_notif\">Đã bình luận về bài đăng của bạn:</string>\n    <string name=\"follow_notif\">Đã bắt đầu theo dõi bạn</string>\n    <string name=\"tagged_notif\">Đã gắn thẻ bạn trong một bài viết</string>\n    <string name=\"request_notif\">Đã yêu cầu theo dõi bạn</string>\n    <string name=\"request_approve\">Duyệt yêu cầu</string>\n    <string name=\"request_reject\">Từ chối yêu cầu</string>\n    <string name=\"share_public_post\">Chia sẻ bài viết công khai này đến…</string>\n    <string name=\"share_private_post\">This is a private post! Share to those who can view it.</string>\n    <string name=\"discover_empty\">Mục này đang bị bỏ trống…</string>\n    <string name=\"update_available\">Đã có bản cập nhật! (%s)</string>\n    <string name=\"updated\">Cảm ơn bạn đã cập nhật Barinsta!</string>\n    <string name=\"crash_title\">Ứng dụng đã bị crash</string>\n    <string name=\"crash_descr\">Ối.. ứng dụng đã bị crash, nhưng đừng lo bạn có thể gửi báo cáo lỗi đến cho nhà phát triển để giúp anh ấy sửa vấn đề. (:</string>\n    <string name=\"action_notif\">Hoạt động</string>\n    <string name=\"action_archive\">Bảo lưu story</string>\n    <string name=\"action_ayml\">Người dùng được đề xuất</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">You have %d notifications</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d người theo dõi</string>\n    <string name=\"activity_count_comments\">%d bình luận</string>\n    <string name=\"activity_count_commentlikes\">%d lượt thích bình luận</string>\n    <string name=\"activity_count_usertags\">%d gắn thẻ</string>\n    <string name=\"activity_count_likes\">%d lượt thích</string>\n    <string name=\"activity_count_poy\">%d Ảnh của bạn</string>\n    <string name=\"activity_count_requests\">%d yêu cầu theo dõi</string>\n    <string name=\"activity_notloggedin\">Bạn đã đăng xuất trước khi ấn vào thông báo này?!</string>\n    <string name=\"feed\">Bảng tin</string>\n    <string name=\"profile\">Hồ sơ</string>\n    <string name=\"more\">Thêm</string>\n    <string name=\"title_dm\">DM</string>\n    <string name=\"number_selected\">%d đã được chọn</string>\n    <string name=\"logout_success\">Đăng xuất thành công!</string>\n    <string name=\"dm_thread_info\">Thông tin</string>\n    <string name=\"mark_as_seen\">Đánh dấu là đã đọc</string>\n    <string name=\"version\">Phiên bản</string>\n    <string name=\"pref_start_screen\">Màn hình khởi động</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">Show keyboard on search</string>\n    <string name=\"pref_category_general\">Chung</string>\n    <string name=\"pref_category_theme\">Giao diện</string>\n    <string name=\"pref_category_downloads\">Tải xuống</string>\n    <string name=\"pref_category_locale\">Địa phương</string>\n    <string name=\"account\">Tài khoản</string>\n    <string name=\"account_hint\">Đăng nhập hiện tại không hoạt động? Chỉ cần thêm tài khoản lại.</string>\n    <string name=\"add_account\">Thêm tài khoản</string>\n    <string name=\"about_category_license\">Giấy phép (Chỉ có tiếng Anh)</string>\n    <string name=\"about_documentation\">Truy cập trang web của chúng tôi</string>\n    <string name=\"about_documentation_summary\">Nhận hỗ trợ, thảo luận, gặp gỡ những người khác và cùng vui vẻ!</string>\n    <string name=\"about_repository\">Xem mã nguồn trên Github</string>\n    <string name=\"about_repository_summary\">Kiểm tra, sao, báo cáo lỗi, đóng góp, và cùng chung vui (một lần nữa)!</string>\n    <string name=\"about_feedback\">Gửi phản hồi bằng email</string>\n    <string name=\"about_category_3pt\">Phân bổ của bên thứ ba</string>\n    <string name=\"reminder\">Nhắc nhở</string>\n    <string name=\"reminder_summary\">Xin hãy dùng ứng dụng này một cách có trách nhiệm. Tải xuống hình ảnh chỉ nên được dùng cho mục đích được chấp thuận bởi luật pháp.</string>\n    <string name=\"light_white_theme\">Trắng</string>\n    <string name=\"dark_black_theme\">Đen</string>\n    <string name=\"light_theme_settings\">Giao diện sáng</string>\n    <string name=\"dark_theme_settings\">Giao diện tối</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">Barista</string>\n    <string name=\"dark_material_dark_theme\">Material Tối</string>\n    <string name=\"added_to_favs\">Đã thêm vào mục Ưa thích!</string>\n    <string name=\"add_to_favorites\">Thêm vào mục yêu thích</string>\n    <string name=\"accounts\">Tài khoản</string>\n    <string name=\"hashtags\">Hashtags</string>\n    <string name=\"locations\">Địa điểm</string>\n    <string name=\"unknown\">Không rõ</string>\n    <string name=\"removed_from_favs\">Đã loại bỏ khỏi danh sách yêu thích!</string>\n    <string name=\"backup_and_restore\">Sao lưu &amp; Khôi phục</string>\n    <string name=\"auto_backup\">Auto Backup</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">Enable Auto Backup</string>\n    <string name=\"manual_backup\">Manual Backup</string>\n    <string name=\"backup_summary\">Sao lưu cài đặt của ứng dụng Barinsta, thông tin đăng nhập, và/hoặc mục yêu thích thành một tệp chữ hoặc được mã hóa cho những lần khôi phục sau.</string>\n    <string name=\"backup_warning\">Nếu bạn đang sao lưu thông tin đăng nhập, hãy xem tệp tin đó như tối mật và hãy giữ nó nơi nào đó an toàn!</string>\n    <string name=\"create_backup\">Tạo tệp sao lưu mới</string>\n    <string name=\"restore_backup\">Khôi phục từ tập tin sao lưu</string>\n    <string name=\"file_chosen_label\">Tập tin:</string>\n    <string name=\"enter_password\">Nhập mật khẩu</string>\n    <string name=\"select_backup_file\">Chọn tệp sao lưu (.zaai/.backup)</string>\n    <string name=\"apply\">Áp dụng</string>\n    <string name=\"save\">Lưu</string>\n    <string name=\"caption\">Chú thích</string>\n    <string name=\"edit_caption\">Chỉnh sửa chú thích</string>\n    <string name=\"translate_caption\">Dịch chú thích</string>\n    <string name=\"player_timeline_desc\">Thanh thời gian của trình phát video</string>\n    <string name=\"liking\">Đang thích…</string>\n    <string name=\"like_unsuccessful\">Không thể thích</string>\n    <string name=\"unlike_unsuccessful\">Không thể bỏ thích</string>\n    <string name=\"unliking\">Đang bỏ thích…</string>\n    <string name=\"controls\">Điều khiển</string>\n    <string name=\"saving\">Đang lưu…</string>\n    <string name=\"removing\">Đang xóa…</string>\n    <string name=\"save_unsuccessful\">Không thể lưu</string>\n    <string name=\"save_remove_unsuccessful\">Không thể xóa</string>\n    <string name=\"downloading\">Đang tải xuống…</string>\n    <string name=\"downloader_downloading_child\">Tải tệp %1$d từ %2$d</string>\n    <string name=\"delete\">Xóa</string>\n    <string name=\"comment\">Bình luận</string>\n    <string name=\"layout\">Giao diện</string>\n    <string name=\"feed_stories\">Story trên bảng tin</string>\n    <string name=\"opening_post\">Opening post…</string>\n    <string name=\"share\">Chia sẻ</string>\n    <string name=\"layout_style\">Phong cach giao diện</string>\n    <string name=\"column_count\">Số cột</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">Hiển thị tên</string>\n    <string name=\"show_avatars\">Hiển thị Avatar</string>\n    <string name=\"avatar_size\">Kích thước avatar</string>\n    <string name=\"corners\">Góc</string>\n    <string name=\"show_grid_gap\">Hiển thị khoảng trống lưới</string>\n    <string name=\"post_not_found\">Không tìm thấy bài viết!</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">Thư viện</string>\n    <string name=\"camera\">Máy ảnh</string>\n    <string name=\"all_photos\">Tất cả ảnh</string>\n    <string name=\"all_media\">Tất cả phương tiện</string>\n    <string name=\"all_videos\">Tất cả Video</string>\n    <string name=\"brightness\">Độ sáng</string>\n    <string name=\"contrast\">Độ tương phản</string>\n    <string name=\"vibrance\">Dao động</string>\n    <string name=\"saturation\">Độ bão hoà</string>\n    <string name=\"sharpen\">Làm sắc nét</string>\n    <string name=\"exposure\">Độ phơi sáng</string>\n    <string name=\"center\">Trung tâm</string>\n    <string name=\"color\">Màu săc</string>\n    <string name=\"start\">Bắt đầu</string>\n    <string name=\"end\">Kết thúc</string>\n    <string name=\"bilateral_blur\">Làm mờ hai bên</string>\n    <string name=\"vignette\">Mờ viền</string>\n    <string name=\"box_blur\">Hộp mờ</string>\n    <string name=\"sepia\">Màu nâu đỏ</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">Đặt lại</string>\n    <string name=\"crop\">Cắt</string>\n    <string name=\"normal\">Bình thường</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">%d lượt xem</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s story</item>\n    </plurals>\n    <string name=\"details\">Chi tiết</string>\n    <string name=\"title\">Tiêu đề</string>\n    <string name=\"members\">Thành viên</string>\n    <string name=\"admin\">Quản trị viên</string>\n    <string name=\"inviter\">Người mời</string>\n    <string name=\"mute_messages\">Tắt thông báo tin nhắn</string>\n    <string name=\"mute_mentions\">Tắt thông báo nhắc đến</string>\n    <string name=\"add_members\">Thêm thành viên</string>\n    <string name=\"search\">Tìm kiếm</string>\n    <string name=\"done\">Xong</string>\n    <string name=\"dms_action_make_admin\">Đặt quản trị viên</string>\n    <string name=\"dms_action_remove_admin\">Xóa quyền quản trị</string>\n    <string name=\"edit_unsuccessful\">Chỉnh sửa thành công</string>\n    <string name=\"message\">Tin nhắn</string>\n    <string name=\"tap_to_remove\">Chạm Để Gỡ</string>\n    <string name=\"forward\">Chuyển tiếp</string>\n    <string name=\"forward_outgoing\">Bạn đã chuyển tiếp một tin nhắn</string>\n    <string name=\"forward_incoming\">Đã chuyển tiếp một tin nhắn</string>\n    <string name=\"add\">Thêm</string>\n    <string name=\"send\">Gửi</string>\n    <string name=\"replying_to_yourself\">Đang trở lời bản thân</string>\n    <string name=\"replying_to_user\">Trả lời đến: %s</string>\n    <string name=\"replied_to_yourself\">Bạn đã trả lời bản thân mình</string>\n    <string name=\"replied_you\">Bạn đã trả lời</string>\n    <string name=\"replied_you_group\">Bạn đã trả lời %s</string>\n    <string name=\"replied_group\">Đã trả lời %s</string>\n    <string name=\"replied_to_you\">Đã trả lời bạn</string>\n    <string name=\"replied_to_themself\">Đã trả lời chính họ</string>\n    <string name=\"reacted_story_outgoing\">Bạn đã bày tỏ cảm xúc story của họ</string>\n    <string name=\"reacted_story_incoming\">Đã bày tỏ cảm xúc story của bạn</string>\n    <string name=\"mentioned_story_outgoing\">Bạn đã nhắc đến họ trong story của mình</string>\n    <string name=\"mentioned_story_incoming\">Đã nhắc đến bạn trong story của họ</string>\n    <string name=\"replied_story_outgoing\">Bạn đã phản hồi story của họ</string>\n    <string name=\"replied_story_incoming\">Đã phản hồi story của bạn</string>\n    <string name=\"raven_image_expired\">Ảnh đã hết hạn</string>\n    <string name=\"raven_image_info\">Ảnh sẽ hết hạn khi được xem</string>\n    <string name=\"raven_video_expired\">Video đã hết hạn</string>\n    <string name=\"raven_video_info\">Video sẽ hết hạn khi được xem</string>\n    <string name=\"raven_msg_expired\">Tin nhắn đã hết hạn</string>\n    <string name=\"raven_msg_info\">Tin nhắn sẽ hết hạn khi được xem</string>\n    <string name=\"story_share\">Story của @%s</string>\n    <string name=\"story_share_highlight\">Story highlight của @%s</string>\n    <string name=\"photo\">Ảnh</string>\n    <string name=\"video\">Video</string>\n    <string name=\"voice_message\">Tin nhắn thoại</string>\n    <string name=\"post\">Bài đăng</string>\n    <string name=\"approval_required_for_new_members\">Yêu cầu phê duyệt để tham gia</string>\n    <string name=\"requests\">Yêu cầu</string>\n    <string name=\"admins_only\">Chỉ dành cho admin</string>\n    <string name=\"added_by\">Đã thêm bởi %s</string>\n    <string name=\"admin_approval_required\">Yêu cầu cho phép của quản trị viên</string>\n    <string name=\"admin_approval_required_description\">Cần có sự cho phép của quản trị viên để thêm thành viên mới vào nhóm</string>\n    <string name=\"dms_action_end\">Kết thúc cuộc trò chuyện</string>\n    <string name=\"dms_action_end_question\">Kết thúc cuộc trò chuyện?</string>\n    <string name=\"dms_action_end_description\">Tất cả thành viên sẽ được xóa khỏi nhóm. Họ vẫn sẽ có thể xem lịch sử cuộc trò chuyện.</string>\n    <string name=\"pending_requests\">Lời mời đang chờ chấp nhận</string>\n    <string name=\"accept_request_from_user\">Chấp nhận yêu cầu từ %1s (%2s)?</string>\n    <string name=\"decline\">Từ chối</string>\n    <string name=\"accept\">Chấp nhận</string>\n    <string name=\"you\">Bạn</string>\n    <string name=\"no_pending_requests\">Không có yêu cầu đang chờ duyệt</string>\n    <string name=\"checking_for_new_messages\">Đang kiểm tra các tin nhắn mới</string>\n    <string name=\"pref_category_stories\">Story</string>\n    <string name=\"pref_category_dm\">DM</string>\n    <string name=\"pref_category_notifications\">Thông báo</string>\n    <string name=\"pref_category_post\">Bài đăng</string>\n    <string name=\"enable_dm_notifications\">Bật thông báo DM</string>\n    <string name=\"enable_dm_auto_refesh\">Tự động làm mới tin nhắn</string>\n    <string name=\"auto_refresh_every\">Tự động làm mới mỗi</string>\n    <string name=\"secs\">giây</string>\n    <string name=\"mins\">phút</string>\n    <string name=\"search_giphy\">Tìm trên GIPHY</string>\n    <string name=\"generic_null_response\">Phản hồi vô giá trị!</string>\n    <string name=\"generic_not_ok_response\">Trạng thái trả lời không ổn định!</string>\n    <string name=\"generic_failed_request\">Yêu cầu thất bại!</string>\n    <string name=\"hint_keyword\">Từ khóa</string>\n    <string name=\"toggle_keyword_filter\">Bật lọc từ khóa</string>\n    <string name=\"edit_keyword_filter\">Chỉnh lọc từ khóa</string>\n    <string name=\"added_keywords\">Đã thêm từ khóa: %s vào danh sách lọc</string>\n    <string name=\"removed_keywords\">Đã loại từ khóa: %s khỏi danh sách lọc</string>\n    <string name=\"marked_as_seen\">Đánh dấu là đã đọc</string>\n    <string name=\"delete_unsuccessful\">Không thể xóa</string>\n    <string name=\"throttle_error\">Đã bị nghẽn bởi Instagram do có quá nhiều yêu cầu API. Xin hãy đợi một lát trước khi thử lại.</string>\n    <string name=\"error\">Lỗi</string>\n    <string name=\"account_logged_out\">Tài khoản này đã đăng xuất.</string>\n    <string name=\"login_required\">Yêu cầu đăng nhập!</string>\n    <string name=\"inactive_user\">Người dùng đang không hoạt động!</string>\n    <string name=\"crash_report_subject\">Báo cáo crash Barinsta</string>\n    <string name=\"crash_report_title\">Chọn một app email để gửi báo cáo</string>\n    <string name=\"not_found\">Không tìm thấy!</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">Bỏ qua bản cập nhật này</string>\n    <string name=\"on_latest_version\">Đã là phiên bản mới nhất</string>\n    <string name=\"tab_order\">Thứ tự màn hình</string>\n    <string name=\"other_tabs\">Những tab khác</string>\n    <string name=\"tab_order_start_next_launch\">Thứ tự tab này sẽ hiệu nghiệm vào lần khởi động kế tiếp</string>\n    <string name=\"dm_remove_warning\">Nếu đã lưu, tất cả tính năng DM sẽ được vô hiệu hóa vào lần khởi động kế tiếp</string>\n    <string name=\"copy_caption\">Sao chép chú thích</string>\n    <string name=\"copy_reply\">Sao chép phản hồi</string>\n    <string name=\"restore\">Restore</string>\n    <string name=\"backup\">Backup</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">No folder selected!</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta folder</string>\n    <string name=\"top\">Hàng đầu</string>\n    <string name=\"recent\">Gần đây</string>\n    <string name=\"clear\">Xóa</string>\n    <string name=\"no_external_map_app\">Không tìm thấy ứng dụng bản đồ!</string>\n    <string name=\"click_to_show_full\">Click to show full like count</string>\n    <string name=\"no_profile_pic_found\">No profile pic found!</string>\n    <string name=\"swipe_up_confirmation\">Are you sure you want to open this link?</string>\n    <string name=\"sending\">Sending…</string>\n    <string name=\"share_via_dm\">Share via DM</string>\n    <string name=\"share_link\">Share link…</string>\n    <string name=\"slide_to_cancel\">Slide to Cancel</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">Stickers</string>\n    <string name=\"story_list\">Story list</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>系统默认</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>自动 / 跟随系统</item>\n        <item>自动 / 跟随电池</item>\n        <item>暗黑</item>\n        <item>明亮</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram 默认 (先未读后已读)</item>\n        <item>由新到旧</item>\n        <item>由旧到新</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>无</item>\n        <item>\\@</item>\n        <item>在</item>\n        <item>于</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>秒</item>\n        <item>分</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">关于</string>\n    <string name=\"action_dms\">私聊</string>\n    <string name=\"action_settings\">设定</string>\n    <string name=\"action_download\">下载</string>\n    <string name=\"action_search\">搜索用户名…</string>\n    <string name=\"action_compare\">比较</string>\n    <string name=\"clipboard_error\">复制文字时出错</string>\n    <string name=\"clipboard_copied\">已复制到剪贴板！</string>\n    <string name=\"report\">报告</string>\n    <string name=\"set_password\">使用密码保护备份</string>\n    <string name=\"password_no_max\">密码</string>\n    <string name=\"ok\">好的</string>\n    <string name=\"yes\">是</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"no\">不</string>\n    <string name=\"confirm\">确定</string>\n    <string name=\"title_favorites\">最爱</string>\n    <string name=\"title_discover\">发现</string>\n    <string name=\"title_comments\">留言</string>\n    <string name=\"title_replies\">回复</string>\n    <string name=\"title_notifications\">活动通知</string>\n    <string name=\"update_check\">启动时检查更新</string>\n    <string name=\"flag_secure\">屏蔽截图及应用预览</string>\n    <string name=\"download_user_folder\">下载帖子到用户名文件夹</string>\n    <string name=\"download_prepend_username\">在文件名前添加用户名</string>\n    <string name=\"mark_as_seen_setting\">查看快拍后将其标记为已读</string>\n    <string name=\"mark_as_seen_setting_summary\">快拍作者会知道您已看过</string>\n    <string name=\"hide_muted_reels_setting\">动态隐藏静音快拍</string>\n    <string name=\"dm_mark_as_seen_setting\">查看私信后将其标记为已读</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">其他成员会知道你看过了</string>\n    <string name=\"autoplay_stories_setting\">自动播放快拍视频</string>\n    <string name=\"story_list_setting\">默认显示快拍列表</string>\n    <string name=\"story_list_setting_summary\">查看快拍时的下方列表</string>\n    <string name=\"activity_setting\">启用活动通知</string>\n    <string name=\"story_sort_setting\">快拍动态排序</string>\n    <string name=\"error_loading_profile\">加载用户资料时出错！用户名是否有效？如果是的话，您的 IP 可能已被 Instagram 所限制。</string>\n    <string name=\"error_loading_hashtag\">加载话题标签出错！名称是否有效？</string>\n    <string name=\"error_loading_location\">加载位置时出错！URL是否有效？</string>\n    <string name=\"error_creating_folders\">创建下载文件夹时出错</string>\n    <string name=\"select_folder\">选择文件夹</string>\n    <string name=\"theme_settings\">主题</string>\n    <string name=\"select_language\">语言</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\n帖子</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s 帖子</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\n粉丝</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\n已关注</string>\n    <string name=\"post_viewer_autoplay_video\">自动播放视频</string>\n    <string name=\"post_viewer_background_play\">在后台继续播放视频</string>\n    <string name=\"post_viewer_background_play_summary\">隐藏应用画面（锁屏、切换应用…）时不暂停视频</string>\n    <string name=\"post_viewer_muted_autoplay\">视频默认静音</string>\n    <string name=\"post_viewer_show_captions\">总是显示帖子标题</string>\n    <string name=\"post_viewer_download_dialog_title\">选择要下载的内容</string>\n    <string name=\"post_viewer_download_current\">当前照片</string>\n    <string name=\"post_viewer_download_album\">整个图集</string>\n    <string name=\"show_stories\">显示快拍</string>\n    <string name=\"no_more_stories\">到底啦！</string>\n    <string name=\"view_post\">查看帖子</string>\n    <string name=\"story_poll\">投票</string>\n    <string name=\"answered_story\">回答成功！</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d 个回答，平均 %s</item>\n    </plurals>\n    <string name=\"slider_answer\">您的回答：%s</string>\n    <string name=\"reply_story\">回复快拍</string>\n    <string name=\"reply_hint\">回复…</string>\n    <string name=\"story_quiz\">测验</string>\n    <string name=\"story_slider\">滑块</string>\n    <string name=\"story_quizzed\">您已回答过了！</string>\n    <string name=\"story_mentions\">提及</string>\n    <string name=\"story_question\">问题</string>\n    <string name=\"priv_acc\">私密账户</string>\n    <string name=\"priv_acc_confirm\">取消关注后，您将无法访问帖子！您确定吗？</string>\n    <string name=\"are_you_sure\">您确定吗？</string>\n    <string name=\"no_acc\">你可以通过右下角的 更多-&gt; 账户 来登录，或者您无须登录即可查看公开账户！</string>\n    <string name=\"empty_acc\">暂未发帖</string>\n    <string name=\"empty_list\">无此类帖！</string>\n    <string name=\"login\">登录</string>\n    <string name=\"logout\">退出</string>\n    <string name=\"logout_summary\">隐身浏览Instagram</string>\n    <string name=\"remove_all_acc\">删除所有帐户</string>\n    <string name=\"remove_all_acc_warning\">这将从应用中移除所有已添加的账户！\\n若要移除其中一个账户，长按账户切换对话框中的那个账户即可。\\n您想要继续吗？</string>\n    <string name=\"time_settings\">日期格式</string>\n    <string name=\"saved_create_collection\">创建新的收藏夹</string>\n    <string name=\"edit_collection\">编辑收藏夹名称</string>\n    <string name=\"delete_collection\">删除收藏夹</string>\n    <string name=\"delete_collection_note\">将要删除的收藏夹中的帖子会在其它收藏夹中保留。</string>\n    <string name=\"add_to_collection\">添加到收藏集…</string>\n    <string name=\"remove_from_collection\">从收藏夹中移除</string>\n    <string name=\"liked\">已赞</string>\n    <string name=\"saved\">已保存</string>\n    <string name=\"tagged\">已标记</string>\n    <string name=\"dm_person\">消息</string>\n    <string name=\"follow\">关注</string>\n    <string name=\"unfollow\">脱粉</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">加入收藏</string>\n    <string name=\"block\">拉黑</string>\n    <string name=\"unblock\">解禁</string>\n    <string name=\"restrict\">限制</string>\n    <string name=\"unrestrict\">放开</string>\n    <string name=\"mute_stories\">隐藏ta的快拍</string>\n    <string name=\"mute_posts\">隐藏ta的帖子</string>\n    <string name=\"unmute_stories\">取消隐藏ta的快拍</string>\n    <string name=\"unmute_posts\">取消隐藏ta的帖子</string>\n    <string name=\"remove_follower\">移除粉丝</string>\n    <string name=\"bio_copy\">复制简介</string>\n    <string name=\"bio_translate\">翻译简介</string>\n    <string name=\"status_mutual\">相互关注</string>\n    <string name=\"status_following\">已关注</string>\n    <string name=\"status_follower\">粉丝</string>\n    <string name=\"map\">地图</string>\n    <string name=\"dialog_export_accounts\">帐户</string>\n    <string name=\"dialog_export_settings\">设置</string>\n    <string name=\"dialog_export_favorites\">收藏</string>\n    <string name=\"dialog_import_success\">成功导入!</string>\n    <string name=\"dialog_import_failed\">导入失败!</string>\n    <string name=\"dialog_export_success\">成功导出!</string>\n    <string name=\"dialog_export_failed\">导出失败！</string>\n    <string name=\"refresh\">刷新</string>\n    <string name=\"get_cookies\">获取 cookies</string>\n    <string name=\"time_settings_title_custom\">使用自定义格式</string>\n    <string name=\"time_settings_title_separator\">分界</string>\n    <string name=\"time_settings_title_time_format\">时间格式</string>\n    <string name=\"time_settings_title_date_format\">日期格式</string>\n    <string name=\"time_settings_title_preview\">预览</string>\n    <string name=\"time_settings_swap_time\">互换时间和日期位置</string>\n    <string name=\"quick_access_cannot_delete_curr\">无法删除正在使用的账户</string>\n    <string name=\"quick_access_confirm_delete\">您确定要删除\\\"%s\\\"吗？</string>\n    <string name=\"open_profile\">打开主页</string>\n    <string name=\"view_story\">查看快拍</string>\n    <string name=\"view_pfp\">查看头像</string>\n    <string name=\"dms_inbox_raven_message_unknown\">不支持此类消息</string>\n    <string name=\"dms_inbox_unsend\">撤回消息</string>\n    <string name=\"dms_inbox_giphy\">在 GIPHY 上查看</string>\n    <string name=\"dms_inbox_shared_post\">%s 分享了 @%s 的帖子</string>\n    <string name=\"dms_inbox_shared_image\">%s 分享了一张图片</string>\n    <string name=\"dms_inbox_shared_video\">%s 分享了一个视频</string>\n    <string name=\"dms_inbox_shared_message\">%s 发送了一条消息</string>\n    <string name=\"dms_inbox_shared_gif\">%s 分享了一个动图</string>\n    <string name=\"dms_inbox_shared_sticker\">%s 发送了一张贴纸</string>\n    <string name=\"dms_inbox_shared_profile\">%s 分享了主页: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s 分享了位置: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s 分享了 @%s 的快拍精选</string>\n    <string name=\"dms_inbox_shared_story\">%s 分享了 @%s 的快拍</string>\n    <string name=\"dms_inbox_shared_voice\">%s 发送了一条语音消息</string>\n    <string name=\"dms_inbox_shared_clip\">%s 分享了 @%s 的短视频</string>\n    <string name=\"dms_inbox_shared_igtv\">%s 分享了 @%s 的IGTV视频</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">您回复了ta的快拍： %s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s 回复了您的快拍： %s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">您对ta的快拍留下了心情： %s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s 对您的快拍留下了心情： %s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">您在快拍中提到了 @%s</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s 在ta的故事中提到了您</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>媒体格式不明</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">消息已过期！</string>\n    <string name=\"dms_inbox_raven_media_delivered\">已送达</string>\n    <string name=\"dms_inbox_raven_media_sent\">已发送</string>\n    <string name=\"dms_inbox_raven_media_opened\">已打开</string>\n    <string name=\"dms_inbox_raven_media_replayed\">已回放</string>\n    <string name=\"dms_inbox_raven_media_sending\">发送中…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">已屏蔽</string>\n    <string name=\"dms_inbox_raven_media_suggested\">为您推荐</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">已截屏</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">无法发送</string>\n    <string name=\"dms_thread_message_hint\">撰写消息…</string>\n    <string name=\"dms_thread_audio_hint\">长按即可录制语音消息</string>\n    <string name=\"dms_thread_updating\">刷新中…</string>\n    <string name=\"dms_action_leave\">离开聊天</string>\n    <string name=\"dms_action_leave_question\">离开此聊天吗？</string>\n    <string name=\"dms_action_kick\">移除成员</string>\n    <string name=\"dms_left_users\">已离开成员</string>\n    <string name=\"dms_ERROR_INVALID_USER\">用户无效</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram 不允许在私信中上传超过 60 秒的视频。</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram 不允许上传超过 60 秒的音频。</string>\n    <string name=\"direct_download_loading\">读取帖子</string>\n    <string name=\"downloader_complete\">下载完成</string>\n    <string name=\"downloader_preparing\">正在准备下载...</string>\n    <string name=\"downloader_downloading_post\">帖子下载中…</string>\n    <string name=\"downloader_downloading_media\">媒体下载中</string>\n    <string name=\"downloader_unknown_error\">出现不详错误!!!</string>\n    <string name=\"downloader_error_creating_folder\">创建文件夹时出错!</string>\n    <string name=\"downloader_error_download_file\">下载文件时出错</string>\n    <string name=\"comment_viewer_translate_comment\">翻译评论</string>\n    <string name=\"comment_viewer_delete_comment\">删除评论</string>\n    <string name=\"followers_type_followers\">粉丝</string>\n    <string name=\"followers_type_following\">关注</string>\n    <string name=\"followers_compare\">比较粉丝和关注</string>\n    <string name=\"followers_both_following\">互粉</string>\n    <string name=\"followers_not_following\">未关注 %s</string>\n    <string name=\"followers_not_follower\">%s 未关注</string>\n    <string name=\"login_error_loading_cookies\">载入 cookies 时出错</string>\n    <string name=\"comment_hint\">撰写一条新评论…</string>\n    <string name=\"liked_notif\">赞了你的帖子</string>\n    <string name=\"comment_notif\">评论了你的帖子</string>\n    <string name=\"follow_notif\">关注了您</string>\n    <string name=\"tagged_notif\">在帖子中标记了你</string>\n    <string name=\"request_notif\">请求关注您</string>\n    <string name=\"request_approve\">同意请求</string>\n    <string name=\"request_reject\">拒绝请求</string>\n    <string name=\"share_public_post\">分享公开帖...</string>\n    <string name=\"share_private_post\">这是一个私密帖！请确定分享对象能查看本帖。</string>\n    <string name=\"discover_empty\">这个板块居然是空的...</string>\n    <string name=\"update_available\">检测到有新版本！ (%s)</string>\n    <string name=\"updated\">感谢阁下更新 Barinsta！</string>\n    <string name=\"crash_title\">应用崩溃了</string>\n    <string name=\"crash_descr\">糟糕.. 应用崩溃了，不过别担心，你可以向开发者发送错误报告来帮助他修复问题。(:</string>\n    <string name=\"action_notif\">通知</string>\n    <string name=\"action_archive\">快拍回忆</string>\n    <string name=\"action_ayml\">推荐用户</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">您有 %d 条通知</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d 位新粉丝</string>\n    <string name=\"activity_count_comments\">%d 个评论回复</string>\n    <string name=\"activity_count_commentlikes\">%d 个评论点赞</string>\n    <string name=\"activity_count_usertags\">%d 个帖子提到了您</string>\n    <string name=\"activity_count_likes\">%d 个赞</string>\n    <string name=\"activity_count_poy\">%d 张照片提到了您</string>\n    <string name=\"activity_count_requests\">%d 关注请求</string>\n    <string name=\"activity_notloggedin\">您在点击此通知前登出了？！</string>\n    <string name=\"feed\">动态</string>\n    <string name=\"profile\">用户</string>\n    <string name=\"more\">更多</string>\n    <string name=\"title_dm\">私信</string>\n    <string name=\"number_selected\">已选择 %d 个</string>\n    <string name=\"logout_success\">成功退出！</string>\n    <string name=\"dm_thread_info\">信息</string>\n    <string name=\"mark_as_seen\">标记为已读</string>\n    <string name=\"version\">版本</string>\n    <string name=\"pref_start_screen\">初始界面</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">搜索时显示键盘</string>\n    <string name=\"pref_category_general\">常规设置</string>\n    <string name=\"pref_category_theme\">主题</string>\n    <string name=\"pref_category_downloads\">下载</string>\n    <string name=\"pref_category_locale\">本地化</string>\n    <string name=\"account\">账户</string>\n    <string name=\"account_hint\">当前登录不管用？添加同一个账户即可修复。</string>\n    <string name=\"add_account\">添加帐户</string>\n    <string name=\"about_category_license\">许可证（仅英文）</string>\n    <string name=\"about_documentation\">访问我们的网站</string>\n    <string name=\"about_documentation_summary\">获取支持、谈天说地、结交好友。祝愉快！</string>\n    <string name=\"about_repository\">在 GitHub 上查看我们的源代码</string>\n    <string name=\"about_repository_summary\">围观、加星、报错、贡献，（再次）祝愉快！</string>\n    <string name=\"about_feedback\">通过电子邮件发送反馈</string>\n    <string name=\"about_category_3pt\">第三方库</string>\n    <string name=\"reminder\">提醒</string>\n    <string name=\"reminder_summary\">请合理利用此工具，并请合法使用下载内容。</string>\n    <string name=\"light_white_theme\">白色</string>\n    <string name=\"dark_black_theme\">黑色</string>\n    <string name=\"light_theme_settings\">浅色主题</string>\n    <string name=\"dark_theme_settings\">深色主题</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">咖啡棕</string>\n    <string name=\"dark_material_dark_theme\">Material 黑</string>\n    <string name=\"added_to_favs\">已收藏</string>\n    <string name=\"add_to_favorites\">加入收藏</string>\n    <string name=\"accounts\">帐户</string>\n    <string name=\"hashtags\">话题</string>\n    <string name=\"locations\">地点</string>\n    <string name=\"unknown\">未知</string>\n    <string name=\"removed_from_favs\">已从收藏夹中删除！</string>\n    <string name=\"backup_and_restore\">备份及恢复</string>\n    <string name=\"auto_backup\">自动备份</string>\n    <string name=\"auto_backup_summary\">从 Android 6 开始，系统的自动备份功能会将所有应用设置、帐户登录设置及收藏夹上传到您的 Google 云端硬盘，以供卸载后重新安装时恢复。</string>\n    <string name=\"auto_backup_warning\">若设备未安装 Google Play 服务，或您已在系统设置中禁用自动备份，本选项将不会生效。禁用本选项不会移除现有的备份。</string>\n    <string name=\"auto_backup_setting\">启用自动备份</string>\n    <string name=\"manual_backup\">手动备份</string>\n    <string name=\"backup_summary\">将应用设置，帐户登录信息，和/或收藏数据备份至纯文本或加密文本，以备日后恢复。</string>\n    <string name=\"backup_warning\">如果您备份登录信息，请务必妥善保管您的备份文件：把它当作密码看待！</string>\n    <string name=\"create_backup\">创建新备份文件</string>\n    <string name=\"restore_backup\">从现有备份文件恢复</string>\n    <string name=\"file_chosen_label\">文件:</string>\n    <string name=\"enter_password\">输入密码</string>\n    <string name=\"select_backup_file\">选择备份文件(.zaai/.backup)</string>\n    <string name=\"apply\">应用</string>\n    <string name=\"save\">保存</string>\n    <string name=\"caption\">说明</string>\n    <string name=\"edit_caption\">编辑说明</string>\n    <string name=\"translate_caption\">翻译说明</string>\n    <string name=\"player_timeline_desc\">视频播放器时间线</string>\n    <string name=\"liking\">正在赞…</string>\n    <string name=\"like_unsuccessful\">点赞失败</string>\n    <string name=\"unlike_unsuccessful\">取消点赞失败</string>\n    <string name=\"unliking\">取消点赞中…</string>\n    <string name=\"controls\">控件</string>\n    <string name=\"saving\">保存中…</string>\n    <string name=\"removing\">删除中……</string>\n    <string name=\"save_unsuccessful\">保存失败</string>\n    <string name=\"save_remove_unsuccessful\">删除失败</string>\n    <string name=\"downloading\">下载中...</string>\n    <string name=\"downloader_downloading_child\">正在下载第 %1$d 项，共 %2$d 项</string>\n    <string name=\"delete\">删除</string>\n    <string name=\"comment\">评论</string>\n    <string name=\"layout\">布局</string>\n    <string name=\"feed_stories\">快拍动态</string>\n    <string name=\"opening_post\">正在打开帖子…</string>\n    <string name=\"share\">分享</string>\n    <string name=\"layout_style\">布局样式</string>\n    <string name=\"column_count\">列数</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">显示名称</string>\n    <string name=\"show_avatars\">显示头像</string>\n    <string name=\"avatar_size\">头像大小</string>\n    <string name=\"corners\">边角</string>\n    <string name=\"show_grid_gap\">显示网格间距</string>\n    <string name=\"post_not_found\">找不到该帖！</string>\n    <string name=\"no_external_app_url\">未找到打开 URL 的应用</string>\n    <string name=\"gallery\">图库</string>\n    <string name=\"camera\">相机</string>\n    <string name=\"all_photos\">所有照片</string>\n    <string name=\"all_media\">所有媒体</string>\n    <string name=\"all_videos\">所有视频</string>\n    <string name=\"brightness\">亮度</string>\n    <string name=\"contrast\">对比度</string>\n    <string name=\"vibrance\">鲜艳度</string>\n    <string name=\"saturation\">饱和度</string>\n    <string name=\"sharpen\">锐化</string>\n    <string name=\"exposure\">曝光度</string>\n    <string name=\"center\">居中</string>\n    <string name=\"color\">色彩</string>\n    <string name=\"start\">开始</string>\n    <string name=\"end\">结束</string>\n    <string name=\"bilateral_blur\">双边模糊</string>\n    <string name=\"vignette\">渐晕</string>\n    <string name=\"box_blur\">方框模糊</string>\n    <string name=\"sepia\">怀旧</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">重置</string>\n    <string name=\"crop\">裁剪</string>\n    <string name=\"normal\">普通</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">%d 次查看</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s 个故事</item>\n    </plurals>\n    <string name=\"details\">详情</string>\n    <string name=\"title\">标题</string>\n    <string name=\"members\">成员</string>\n    <string name=\"admin\">管理员</string>\n    <string name=\"inviter\">邀请人</string>\n    <string name=\"mute_messages\">将消息静音</string>\n    <string name=\"mute_mentions\">将提及静音</string>\n    <string name=\"add_members\">添加成员</string>\n    <string name=\"search\">搜索</string>\n    <string name=\"done\">完成</string>\n    <string name=\"dms_action_make_admin\">设为管理员</string>\n    <string name=\"dms_action_remove_admin\">移除管理员</string>\n    <string name=\"edit_unsuccessful\">编辑失败</string>\n    <string name=\"message\">消息</string>\n    <string name=\"tap_to_remove\">点击删除</string>\n    <string name=\"forward\">转发</string>\n    <string name=\"forward_outgoing\">您转发了一条消息</string>\n    <string name=\"forward_incoming\">转发了一条消息</string>\n    <string name=\"add\">添加</string>\n    <string name=\"send\">发送</string>\n    <string name=\"replying_to_yourself\">回复给自己</string>\n    <string name=\"replying_to_user\">回复 %s</string>\n    <string name=\"replied_to_yourself\">您回复了自己</string>\n    <string name=\"replied_you\">您回复了</string>\n    <string name=\"replied_you_group\">您回复了 %s</string>\n    <string name=\"replied_group\">回复了 %s</string>\n    <string name=\"replied_to_you\">回复了您</string>\n    <string name=\"replied_to_themself\">回复给ta自己</string>\n    <string name=\"reacted_story_outgoing\">您对ta的快拍留下了心情</string>\n    <string name=\"reacted_story_incoming\">对您的快拍留下了心情</string>\n    <string name=\"mentioned_story_outgoing\">您在快拍中提到了ta</string>\n    <string name=\"mentioned_story_incoming\">在ta的故事中提到了您</string>\n    <string name=\"replied_story_outgoing\">您回复了ta的快拍</string>\n    <string name=\"replied_story_incoming\">回复了您的快拍</string>\n    <string name=\"raven_image_expired\">图片已过期</string>\n    <string name=\"raven_image_info\">图片阅后即焚</string>\n    <string name=\"raven_video_expired\">视频已过期</string>\n    <string name=\"raven_video_info\">视频阅后即焚</string>\n    <string name=\"raven_msg_expired\">消息已过期</string>\n    <string name=\"raven_msg_info\">消息阅后即焚</string>\n    <string name=\"story_share\">\\@%s 的快拍</string>\n    <string name=\"story_share_highlight\">\\@%s 的快拍精选</string>\n    <string name=\"photo\">照片</string>\n    <string name=\"video\">视频</string>\n    <string name=\"voice_message\">语音消息</string>\n    <string name=\"post\">帖子</string>\n    <string name=\"approval_required_for_new_members\">需要批准才能加入</string>\n    <string name=\"requests\">请求</string>\n    <string name=\"admins_only\">仅限管理员</string>\n    <string name=\"added_by\">由 %s 添加</string>\n    <string name=\"admin_approval_required\">需要管理员批准</string>\n    <string name=\"admin_approval_required_description\">添加新成员到群组需要管理员批准</string>\n    <string name=\"dms_action_end\">结束聊天</string>\n    <string name=\"dms_action_end_question\">结束聊天?</string>\n    <string name=\"dms_action_end_description\">将踢出群组中的所有成员。他们仍然可以查看聊天记录。</string>\n    <string name=\"pending_requests\">待处理请求</string>\n    <string name=\"accept_request_from_user\">接受来自 %1s (%2s) 的请求？</string>\n    <string name=\"decline\">拒绝</string>\n    <string name=\"accept\">接受</string>\n    <string name=\"you\">您</string>\n    <string name=\"no_pending_requests\">无待处理请求</string>\n    <string name=\"checking_for_new_messages\">正在检查新信息</string>\n    <string name=\"pref_category_stories\">快拍</string>\n    <string name=\"pref_category_dm\">私信</string>\n    <string name=\"pref_category_notifications\">通知</string>\n    <string name=\"pref_category_post\">帖子</string>\n    <string name=\"enable_dm_notifications\">启用私信通知</string>\n    <string name=\"enable_dm_auto_refesh\">自动刷新私信消息</string>\n    <string name=\"auto_refresh_every\">自动刷新间隔</string>\n    <string name=\"secs\">秒</string>\n    <string name=\"mins\">分</string>\n    <string name=\"search_giphy\">搜索 GIPHY</string>\n    <string name=\"generic_null_response\">回应为空！</string>\n    <string name=\"generic_not_ok_response\">回应状态错误！</string>\n    <string name=\"generic_failed_request\">请求失败</string>\n    <string name=\"hint_keyword\">关键词</string>\n    <string name=\"toggle_keyword_filter\">启用关键词过滤器</string>\n    <string name=\"edit_keyword_filter\">编辑关键词过滤器</string>\n    <string name=\"added_keywords\">已添加关键词: %s 至过滤列表</string>\n    <string name=\"removed_keywords\">已移除关键词: %s 至过滤列表</string>\n    <string name=\"marked_as_seen\">已标记为已读</string>\n    <string name=\"delete_unsuccessful\">删除失败</string>\n    <string name=\"throttle_error\">您发送的API请求过多。请等待一段时间后重试。</string>\n    <string name=\"error\">错误</string>\n    <string name=\"account_logged_out\">您已经退出。</string>\n    <string name=\"login_required\">需要登录！</string>\n    <string name=\"inactive_user\">账户已冻结！</string>\n    <string name=\"crash_report_subject\">Barinsta 崩溃报告</string>\n    <string name=\"crash_report_title\">选择一个电子邮件应用来发送崩溃日志</string>\n    <string name=\"not_found\">未找到所要的内容！</string>\n    <string name=\"rate_limit\">您的 IP 已被 Instagram 设限。 &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;了解更多&lt;/a&gt;</string>\n    <string name=\"skip_update\">跳过本次更新</string>\n    <string name=\"on_latest_version\">您现在安装的是最新版本</string>\n    <string name=\"tab_order\">页面顺序</string>\n    <string name=\"other_tabs\">其它页面</string>\n    <string name=\"tab_order_start_next_launch\">页面顺序将在下次启动时生效</string>\n    <string name=\"dm_remove_warning\">若您如此保存，所有私信功能将会在下次启动时被禁用</string>\n    <string name=\"copy_caption\">复制说明</string>\n    <string name=\"copy_reply\">复制回复</string>\n    <string name=\"restore\">恢复</string>\n    <string name=\"backup\">备份</string>\n    <string name=\"dir_select_default_message\">请选择一个 Barinsta 可以存储媒体及临时文件的目录。\\n\\n您可以稍后在更多 &gt; 设置 &gt; 下载中更改此文件夹。</string>\n    <string name=\"dir_select_reselect_message\">Android 已经更改应用访问设备中文件和目录的方式。目前 Barinsta 没有权限访问以下文件夹：</string>\n    <string name=\"dir_select_permission_revoked_message\">之前选择的文件夹的访问权限已被系统撤销：</string>\n    <string name=\"dir_select_folder_not_exist\">之前选择的文件夹已不存在：</string>\n    <string name=\"dir_select_message2\">请点击下面的按钮，来重新选择该目录或选择一个新目录。</string>\n    <string name=\"select_a_folder\">未选择文件夹！</string>\n    <string name=\"dir_select_no_download_folder\">请选择设备存储中的一个目录，而不是侧边栏上的类别。\\n(%s)</string>\n    <string name=\"dir_select_success_message\">设置成功！请稍候。应用程序启动中…</string>\n    <string name=\"barinsta_folder\">Barinsta 文件夹</string>\n    <string name=\"top\">热门</string>\n    <string name=\"recent\">近期搜索</string>\n    <string name=\"clear\">清空</string>\n    <string name=\"no_external_map_app\">未找到地图应用！</string>\n    <string name=\"click_to_show_full\">点击显示完整点赞计数</string>\n    <string name=\"no_profile_pic_found\">未找到用户头像！</string>\n    <string name=\"swipe_up_confirmation\">您确定要打开此链接吗？</string>\n    <string name=\"sending\">正在发送…</string>\n    <string name=\"share_via_dm\">分享至私信</string>\n    <string name=\"share_link\">分享链接…</string>\n    <string name=\"slide_to_cancel\">滑动以取消</string>\n    <string name=\"disable_screen_transitions\">禁用屏幕过渡动画</string>\n    <string name=\"invalid_format\">格式无效</string>\n    <string name=\"no_directory_picker_activity\">Barinsta 无法启动 Android 自带的文件管理器。请确保您已将其安装并启用。</string>\n    <string name=\"story_stickers\">贴纸</string>\n    <string name=\"story_list\">快拍列表</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"languages\">\n        <item>系統預設</item>\n        <item translatable=\"false\">English</item>\n        <item translatable=\"false\">Français</item>\n        <item translatable=\"false\">Español</item>\n        <item translatable=\"false\">简体中文</item>\n        <item translatable=\"false\">Bahasa Indonesia</item>\n        <item translatable=\"false\">Italiano</item>\n        <item translatable=\"false\">Deutsch</item>\n        <item translatable=\"false\">Polski</item>\n        <item translatable=\"false\">Türkçe</item>\n        <item translatable=\"false\">Português (Brasil)</item>\n        <item translatable=\"false\">پارسی</item>\n        <item translatable=\"false\">Македонски</item>\n        <item translatable=\"false\">Tiếng Việt</item>\n        <item translatable=\"false\">繁體中文</item>\n        <item translatable=\"false\">Català</item>\n        <item translatable=\"false\">Русский</item>\n        <item translatable=\"false\">हिन्दी</item>\n        <item translatable=\"false\">Nederlands</item>\n        <item translatable=\"false\">Slovenčina</item>\n        <item translatable=\"false\">日本語</item>\n        <item translatable=\"false\">Ελληνικά</item>\n        <item translatable=\"false\">Euskara</item>\n        <item translatable=\"false\">Svenska</item>\n        <item translatable=\"false\">한국어</item>\n        <item translatable=\"false\">العربية</item>\n    </string-array>\n    <string-array name=\"theme_presets\">\n        <item>自動/跟隨系統</item>\n        <item>自動/跟隨電量</item>\n        <item>暗黑</item>\n        <item>明亮</item>\n    </string-array>\n    <string-array name=\"story_sorts\">\n        <item>Instagram 預設 (先顯示未讀、再顯示已讀)</item>\n        <item>從最新到最舊</item>\n        <item>從最舊到最新</item>\n    </string-array>\n    <string-array name=\"separator_presets\">\n        <item>無</item>\n        <item>\\@</item>\n        <item>於</item>\n        <item>在</item>\n        <item>\\|</item>\n        <item>-</item>\n    </string-array>\n    <string-array name=\"dm_auto_refresh_freq_units\">\n        <item>秒</item>\n        <item>分</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"action_about\">關於</string>\n    <string name=\"action_dms\">訊息</string>\n    <string name=\"action_settings\">設定</string>\n    <string name=\"action_download\">下載</string>\n    <string name=\"action_search\">搜尋用戶名…</string>\n    <string name=\"action_compare\">比較</string>\n    <string name=\"clipboard_error\">複製文字時出錯</string>\n    <string name=\"clipboard_copied\">已複製至剪貼簿！</string>\n    <string name=\"report\">回報</string>\n    <string name=\"set_password\">使用密碼保護備份</string>\n    <string name=\"password_no_max\">密碼</string>\n    <string name=\"ok\">確定</string>\n    <string name=\"yes\">是</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"no\">否</string>\n    <string name=\"confirm\">確認</string>\n    <string name=\"title_favorites\">收藏</string>\n    <string name=\"title_discover\">探索</string>\n    <string name=\"title_comments\">評論</string>\n    <string name=\"title_replies\">回覆</string>\n    <string name=\"title_notifications\">動態</string>\n    <string name=\"update_check\">啟動時檢查更新</string>\n    <string name=\"flag_secure\">阻擋截圖及應用程式預覽</string>\n    <string name=\"download_user_folder\">將貼文下載到用戶名資料夾</string>\n    <string name=\"download_prepend_username\">在檔案名稱前添加用戶名</string>\n    <string name=\"mark_as_seen_setting\">檢視完限時動態後標記為已讀</string>\n    <string name=\"mark_as_seen_setting_summary\">限時動態的作者會知道您已查看了此限時動態</string>\n    <string name=\"hide_muted_reels_setting\">隱藏已隱藏的限時動態</string>\n    <string name=\"dm_mark_as_seen_setting\">檢視完訊息後標記為已讀</string>\n    <string name=\"dm_mark_as_seen_setting_summary\">其他成員會知道您查看了此訊息</string>\n    <string name=\"autoplay_stories_setting\">自動播放限時動態</string>\n    <string name=\"story_list_setting\">Display story list by default</string>\n    <string name=\"story_list_setting_summary\">For viewing stories</string>\n    <string name=\"activity_setting\">啟用活動通知</string>\n    <string name=\"story_sort_setting\">動態貼文排序</string>\n    <string name=\"error_loading_profile\">加載用戶資料時出錯！請檢查輸入的用戶名是否無誤。如果無誤的話，您的 Instagram 功能可能已被暫時受限。</string>\n    <string name=\"error_loading_hashtag\">載入 Hashtag 時出錯。請檢查此名稱是否有誤。</string>\n    <string name=\"error_loading_location\">載入此位置時出錯。請檢查該連結是否有誤。</string>\n    <string name=\"error_creating_folders\">建立下載資料夾時出錯！</string>\n    <string name=\"select_folder\">選擇資料夾</string>\n    <string name=\"theme_settings\">主題</string>\n    <string name=\"select_language\">語言</string>\n    <plurals name=\"main_posts_count\">\n        <item quantity=\"other\">%s\\n貼文</item>\n    </plurals>\n    <plurals name=\"main_posts_count_inline\">\n        <item quantity=\"other\">%s 貼文</item>\n    </plurals>\n    <plurals name=\"main_posts_followers\">\n        <item quantity=\"other\">%s\\n追蹤者</item>\n    </plurals>\n    <string name=\"main_posts_following\">%s\\n追蹤中</string>\n    <string name=\"post_viewer_autoplay_video\">自動播放影片</string>\n    <string name=\"post_viewer_background_play\">於背景繼續播放影片</string>\n    <string name=\"post_viewer_background_play_summary\">隱藏應用程式 (如鎖屏、切換應用程式) 時，繼續播放影片</string>\n    <string name=\"post_viewer_muted_autoplay\">永遠自動靜音影片</string>\n    <string name=\"post_viewer_show_captions\">永遠顯示貼文標題</string>\n    <string name=\"post_viewer_download_dialog_title\">選擇要下載的內容</string>\n    <string name=\"post_viewer_download_current\">當前照片</string>\n    <string name=\"post_viewer_download_album\">整個相簿</string>\n    <string name=\"show_stories\">顯示限時動態</string>\n    <string name=\"no_more_stories\">沒有限時動態了！</string>\n    <string name=\"view_post\">查看文章</string>\n    <string name=\"story_poll\">投票</string>\n    <string name=\"answered_story\">回答成功！</string>\n    <plurals name=\"slider_info\" comment=\"For slider stickers in stories, eg. 3 responses averaging 17.38%\">\n        <item quantity=\"other\">%d 個回應，平均 %s</item>\n    </plurals>\n    <string name=\"slider_answer\">你的答案: %s</string>\n    <string name=\"reply_story\">回覆限時動態</string>\n    <string name=\"reply_hint\">回覆…</string>\n    <string name=\"story_quiz\">測驗</string>\n    <string name=\"story_slider\">滑桿</string>\n    <string name=\"story_quizzed\">您已經回答了！</string>\n    <string name=\"story_mentions\">提及</string>\n    <string name=\"story_question\">問題</string>\n    <string name=\"priv_acc\">此帳戶為私人帳戶</string>\n    <string name=\"priv_acc_confirm\">取消關注後，您將無法閱讀人家的貼文！你確定嗎？</string>\n    <string name=\"are_you_sure\">您確定嗎?</string>\n    <string name=\"no_acc\">您可以透過右下角的 「更多」 -&gt; 「新增帳號」 來登入您的帳號，或者以匿名的身分查看公共帳戶！</string>\n    <string name=\"empty_acc\">此帳戶尚未張貼任何文章</string>\n    <string name=\"empty_list\">找不到此貼文！</string>\n    <string name=\"login\">登入</string>\n    <string name=\"logout\">登出</string>\n    <string name=\"logout_summary\">匿名瀏覽 Instagram</string>\n    <string name=\"remove_all_acc\">刪除所有帳戶</string>\n    <string name=\"remove_all_acc_warning\">此動作將會自本程式移除所有已登入的帳號！\\n如果您只想要移除單一帳號，在切換用戶按鈕上長按即可。\\n你確定要繼續嗎？</string>\n    <string name=\"time_settings\">日期格式</string>\n    <string name=\"saved_create_collection\">建立新的收藏</string>\n    <string name=\"edit_collection\">編輯收藏名稱</string>\n    <string name=\"delete_collection\">刪除收藏</string>\n    <string name=\"delete_collection_note\">All posts contained in the deleted collection will remain in other collections.</string>\n    <string name=\"add_to_collection\">新增至收藏集…</string>\n    <string name=\"remove_from_collection\">從收藏中移除</string>\n    <string name=\"liked\">已按讚</string>\n    <string name=\"saved\">已儲存</string>\n    <string name=\"tagged\">已標記</string>\n    <string name=\"dm_person\">訊息</string>\n    <string name=\"follow\">追蹤</string>\n    <string name=\"unfollow\">取消追蹤</string>\n    <string name=\"favorite_short\" comment=\"Adjective, not verb\">收藏</string>\n    <string name=\"block\">封鎖</string>\n    <string name=\"unblock\">解除封鎖</string>\n    <string name=\"restrict\">限制</string>\n    <string name=\"unrestrict\">移除限制</string>\n    <string name=\"mute_stories\">隱藏限時動態</string>\n    <string name=\"mute_posts\">隱藏貼文</string>\n    <string name=\"unmute_stories\">取消隱藏限時動態</string>\n    <string name=\"unmute_posts\">取消隱藏貼文</string>\n    <string name=\"remove_follower\">移除追蹤者</string>\n    <string name=\"bio_copy\">複製簡介</string>\n    <string name=\"bio_translate\">翻譯簡介</string>\n    <string name=\"status_mutual\">正彼此追蹤</string>\n    <string name=\"status_following\">追蹤中</string>\n    <string name=\"status_follower\">追蹤者</string>\n    <string name=\"map\">地圖</string>\n    <string name=\"dialog_export_accounts\">帳號</string>\n    <string name=\"dialog_export_settings\">設定</string>\n    <string name=\"dialog_export_favorites\">收藏</string>\n    <string name=\"dialog_import_success\">已成功匯入！</string>\n    <string name=\"dialog_import_failed\">匯入失敗！</string>\n    <string name=\"dialog_export_success\">已成功匯出！</string>\n    <string name=\"dialog_export_failed\">匯出失敗！</string>\n    <string name=\"refresh\">重新整理</string>\n    <string name=\"get_cookies\">取得 Cookies</string>\n    <string name=\"time_settings_title_custom\">使用自定義格式</string>\n    <string name=\"time_settings_title_separator\">分隔</string>\n    <string name=\"time_settings_title_time_format\">時間格式</string>\n    <string name=\"time_settings_title_date_format\">日期格式</string>\n    <string name=\"time_settings_title_preview\">預覽</string>\n    <string name=\"time_settings_swap_time\">交換時間和日期位置</string>\n    <string name=\"quick_access_cannot_delete_curr\">無法刪除當前正在使用的帳戶</string>\n    <string name=\"quick_access_confirm_delete\">您確定要刪除 %s 嗎？</string>\n    <string name=\"open_profile\">開啟個人檔案</string>\n    <string name=\"view_story\">查看限時動態</string>\n    <string name=\"view_pfp\">查看大頭貼照</string>\n    <string name=\"dms_inbox_raven_message_unknown\">未支援的訊息類型</string>\n    <string name=\"dms_inbox_unsend\">收回訊息</string>\n    <string name=\"dms_inbox_giphy\">在 GIPHY 上檢視</string>\n    <string name=\"dms_inbox_shared_post\">%s 分享了 @%s 的貼文</string>\n    <string name=\"dms_inbox_shared_image\">%s 分享了一張圖片</string>\n    <string name=\"dms_inbox_shared_video\">%s 分享了一段影片</string>\n    <string name=\"dms_inbox_shared_message\">%s 傳送了一則訊息</string>\n    <string name=\"dms_inbox_shared_gif\">%s 分享了一個動圖</string>\n    <string name=\"dms_inbox_shared_sticker\">%s 分享了一張貼圖</string>\n    <string name=\"dms_inbox_shared_profile\">%s 分享了頁面: @%s</string>\n    <string name=\"dms_inbox_shared_location\">%s 分享了位置: %s</string>\n    <string name=\"dms_inbox_shared_highlight\">%s 分享了 @%s 的精選限時</string>\n    <string name=\"dms_inbox_shared_story\">%s 分享了 @%s 的限時動態</string>\n    <string name=\"dms_inbox_shared_voice\">%s 傳送了一則語音訊息</string>\n    <string name=\"dms_inbox_shared_clip\">%s 分享了 @%s 的短片</string>\n    <string name=\"dms_inbox_shared_igtv\">%s 分享了 @%s 的 IGTV 影片</string>\n    <string name=\"dms_inbox_replied_story_outgoing\">你回覆了對方的限時動態：%s</string>\n    <string name=\"dms_inbox_replied_story_incoming\">%s 回覆了你的限時動態：%s</string>\n    <string name=\"dms_inbox_reacted_story_outgoing\">你對對方的限時動態傳達了心情：%s</string>\n    <string name=\"dms_inbox_reacted_story_incoming\">%s 對你的限時動態傳達了心情：%s</string>\n    <string name=\"dms_inbox_mentioned_story_outgoing\">你在限時動態中提及了 @%s</string>\n    <string name=\"dms_inbox_mentioned_story_incoming\">%s 在限時動態中提及了你</string>\n    <string name=\"dms_inbox_raven_media_unknown\"><i>未知的媒體類型</i></string>\n    <string name=\"dms_inbox_raven_media_expired\">媒體已過期！</string>\n    <string name=\"dms_inbox_raven_media_delivered\">已傳送</string>\n    <string name=\"dms_inbox_raven_media_sent\">已傳送</string>\n    <string name=\"dms_inbox_raven_media_opened\">已開啟</string>\n    <string name=\"dms_inbox_raven_media_replayed\">已重播</string>\n    <string name=\"dms_inbox_raven_media_sending\">正在發送…</string>\n    <string name=\"dms_inbox_raven_media_blocked\">已封鎖</string>\n    <string name=\"dms_inbox_raven_media_suggested\">建議</string>\n    <string name=\"dms_inbox_raven_media_screenshot\">已截圖</string>\n    <string name=\"dms_inbox_raven_media_cant_deliver\">無法傳送</string>\n    <string name=\"dms_thread_message_hint\">撰寫訊息…</string>\n    <string name=\"dms_thread_audio_hint\">長按即可錄製語音訊息</string>\n    <string name=\"dms_thread_updating\">更新中…</string>\n    <string name=\"dms_action_leave\">離開聊天</string>\n    <string name=\"dms_action_leave_question\">離開此聊天室？</string>\n    <string name=\"dms_action_kick\">踢除</string>\n    <string name=\"dms_left_users\">已離開的用戶</string>\n    <string name=\"dms_ERROR_INVALID_USER\">用戶無效</string>\n    <string name=\"dms_ERROR_VIDEO_TOO_LONG\">Instagram 不允許在私訊中上傳超過 60 秒的影片。</string>\n    <string name=\"dms_ERROR_AUDIO_TOO_LONG\">Instagram 不允許在私訊中上傳超過 60 秒的音訊。</string>\n    <string name=\"direct_download_loading\">獲取文章中</string>\n    <string name=\"downloader_complete\">下載完成</string>\n    <string name=\"downloader_preparing\">正在准備下載…</string>\n    <string name=\"downloader_downloading_post\">下載貼文中…</string>\n    <string name=\"downloader_downloading_media\">下載媒體中</string>\n    <string name=\"downloader_unknown_error\">發生未知錯誤！</string>\n    <string name=\"downloader_error_creating_folder\">建立資料夾時出錯！</string>\n    <string name=\"downloader_error_download_file\">下載檔案時出錯</string>\n    <string name=\"comment_viewer_translate_comment\">翻譯評論</string>\n    <string name=\"comment_viewer_delete_comment\">刪除評論</string>\n    <string name=\"followers_type_followers\">追蹤者</string>\n    <string name=\"followers_type_following\">追蹤中</string>\n    <string name=\"followers_compare\">比較粉絲 &amp; 關注</string>\n    <string name=\"followers_both_following\">正彼此追蹤</string>\n    <string name=\"followers_not_following\">尚未追蹤 %s</string>\n    <string name=\"followers_not_follower\">%s 尚未追蹤</string>\n    <string name=\"login_error_loading_cookies\">載入 Cookie 時出錯</string>\n    <string name=\"comment_hint\">留言回應…</string>\n    <string name=\"liked_notif\">對您的貼文說讚</string>\n    <string name=\"comment_notif\">已回應你的貼文：</string>\n    <string name=\"follow_notif\">已開始追蹤您</string>\n    <string name=\"tagged_notif\">在貼文中標記了你</string>\n    <string name=\"request_notif\">追蹤要求</string>\n    <string name=\"request_approve\">接受要求</string>\n    <string name=\"request_reject\">略過要求</string>\n    <string name=\"share_public_post\">分享這則公開貼文給…</string>\n    <string name=\"share_private_post\">這個是私人貼文！分享給可以查看的人。</string>\n    <string name=\"discover_empty\">這個類別是空的…</string>\n    <string name=\"update_available\">有可用的更新！ （%s）</string>\n    <string name=\"updated\">感謝您更新 Barinsta！</string>\n    <string name=\"crash_title\">程式出錯了</string>\n    <string name=\"crash_descr\">喔噢！程式當掉了！別擔心，您可以向開發人員發送錯誤報告以協助解決此問題！ (:</string>\n    <string name=\"action_notif\">動態</string>\n    <string name=\"action_archive\">動態回憶</string>\n    <string name=\"action_ayml\">推薦用戶</string>\n    <plurals name=\"activity_count_total\">\n        <item quantity=\"other\">您有 %d 個通知</item>\n    </plurals>\n    <string name=\"activity_count_relationship\">%d 個追蹤者</string>\n    <string name=\"activity_count_comments\">%d 個評論</string>\n    <string name=\"activity_count_commentlikes\">%d 個評論的讚</string>\n    <string name=\"activity_count_usertags\">%d 個用戶標記</string>\n    <string name=\"activity_count_likes\">%d 個讚</string>\n    <string name=\"activity_count_poy\">%d 張提及了您的照片</string>\n    <string name=\"activity_count_requests\">%d 個追蹤請求</string>\n    <string name=\"activity_notloggedin\">你怎麼在點擊此通知前登出的？！</string>\n    <string name=\"feed\">貼文</string>\n    <string name=\"profile\">檔案</string>\n    <string name=\"more\">更多</string>\n    <string name=\"title_dm\">訊息</string>\n    <string name=\"number_selected\">已選 %d</string>\n    <string name=\"logout_success\">已成功登出！</string>\n    <string name=\"dm_thread_info\">資訊</string>\n    <string name=\"mark_as_seen\">標記為已讀</string>\n    <string name=\"version\">版本</string>\n    <string name=\"pref_start_screen\">開始畫面</string>\n    <string name=\"pref_search_focus_keyboard\" comment=\"basically bring up the keyboard immediately when someone does search\">搜尋時顯示鍵盤</string>\n    <string name=\"pref_category_general\">一般</string>\n    <string name=\"pref_category_theme\">主題</string>\n    <string name=\"pref_category_downloads\">下載</string>\n    <string name=\"pref_category_locale\">區域設定</string>\n    <string name=\"account\">帳號</string>\n    <string name=\"account_hint\">目前登入的帳號有問題嗎？重新登入看看吧！</string>\n    <string name=\"add_account\">新增帳號</string>\n    <string name=\"about_category_license\">使用條款 (英文)</string>\n    <string name=\"about_documentation\">造訪我們的網站</string>\n    <string name=\"about_documentation_summary\">取得支援、與他人討論、結識他人並享受樂趣！</string>\n    <string name=\"about_repository\">在 GitHub 上查看我們的開源程式碼</string>\n    <string name=\"about_repository_summary\">審核、Star、回報錯誤、做出貢獻並從中再次享受樂趣！</string>\n    <string name=\"about_feedback\">透過電子郵件發送意見</string>\n    <string name=\"about_category_3pt\">第三方程式庫</string>\n    <string name=\"reminder\">提醒</string>\n    <string name=\"reminder_summary\">請勿濫用此應用程式。下載的圖片只能用於法律允許的目的。</string>\n    <string name=\"light_white_theme\">白色</string>\n    <string name=\"dark_black_theme\">黑色</string>\n    <string name=\"light_theme_settings\">淺色主題</string>\n    <string name=\"dark_theme_settings\">暗色主題</string>\n    <string name=\"light_barinsta_theme\" comment=\"Yes, this one is Barista (the theme), you can also substitute it with other coffee-related words\">咖啡棕</string>\n    <string name=\"dark_material_dark_theme\">暗黑 Material</string>\n    <string name=\"added_to_favs\">已收藏</string>\n    <string name=\"add_to_favorites\">加入收藏</string>\n    <string name=\"accounts\">帳號</string>\n    <string name=\"hashtags\">主題標籤</string>\n    <string name=\"locations\">地點</string>\n    <string name=\"unknown\">未知</string>\n    <string name=\"removed_from_favs\">已從收藏中移除!</string>\n    <string name=\"backup_and_restore\">備份與還原</string>\n    <string name=\"auto_backup\">自動備份</string>\n    <string name=\"auto_backup_summary\">Starting from Android 6, Android\\'s Auto Backup feature will upload all app settings, account login data, and favorites onto your Google Drive, which can be restored by reinstalling the app after uninstallation.</string>\n    <string name=\"auto_backup_warning\">This preference has no effect if Google Play Services is not present, or if Auto Backup is disabled from your device settings. Disabling here does not erase existing backups.</string>\n    <string name=\"auto_backup_setting\">啟用自動備份</string>\n    <string name=\"manual_backup\">手動備份</string>\n    <string name=\"backup_summary\">備份 Barinsta 的設定、登入資料、及收藏中的內容至純文字或加密後的檔案，以便日後還原。</string>\n    <string name=\"backup_warning\">如果您選擇備份登入相關資訊，請將此檔案視為密碼看待，並妥善保存！</string>\n    <string name=\"create_backup\">建立備份檔</string>\n    <string name=\"restore_backup\">以備份檔還原</string>\n    <string name=\"file_chosen_label\">檔案：</string>\n    <string name=\"enter_password\">輸入密碼</string>\n    <string name=\"select_backup_file\">選擇一個備份文件（.zaai / .backup）</string>\n    <string name=\"apply\">套用</string>\n    <string name=\"save\">儲存</string>\n    <string name=\"caption\">說明</string>\n    <string name=\"edit_caption\">編輯說明</string>\n    <string name=\"translate_caption\">翻譯說明</string>\n    <string name=\"player_timeline_desc\">影片播放器時間軸</string>\n    <string name=\"liking\">按讚中…</string>\n    <string name=\"like_unsuccessful\">按讚失敗</string>\n    <string name=\"unlike_unsuccessful\">無法收回讚</string>\n    <string name=\"unliking\">正在收回讚…</string>\n    <string name=\"controls\">控制</string>\n    <string name=\"saving\">儲存中…</string>\n    <string name=\"removing\">刪除中…</string>\n    <string name=\"save_unsuccessful\">儲存失敗</string>\n    <string name=\"save_remove_unsuccessful\">刪除失敗</string>\n    <string name=\"downloading\">下載中…</string>\n    <string name=\"downloader_downloading_child\">下載 %2$d 中的 %1$d</string>\n    <string name=\"delete\">刪除</string>\n    <string name=\"comment\">評論</string>\n    <string name=\"layout\">佈局</string>\n    <string name=\"feed_stories\">限時動態</string>\n    <string name=\"opening_post\">開啟貼文中…</string>\n    <string name=\"share\">分享</string>\n    <string name=\"layout_style\">佈局樣式</string>\n    <string name=\"column_count\">欄位數量</string>\n    <string name=\"two\">2</string>\n    <string name=\"three\">3</string>\n    <string name=\"show_names\">顯示名稱</string>\n    <string name=\"show_avatars\">顯示大頭貼</string>\n    <string name=\"avatar_size\">大頭貼大小</string>\n    <string name=\"corners\">邊角</string>\n    <string name=\"show_grid_gap\">網格間保留間距</string>\n    <string name=\"post_not_found\">貼文不存在！</string>\n    <string name=\"no_external_app_url\">No apps for opening URLs found</string>\n    <string name=\"gallery\">圖庫</string>\n    <string name=\"camera\">相機</string>\n    <string name=\"all_photos\">所有照片</string>\n    <string name=\"all_media\">所有媒體</string>\n    <string name=\"all_videos\">所有影片</string>\n    <string name=\"brightness\">亮度</string>\n    <string name=\"contrast\">對比度</string>\n    <string name=\"vibrance\">鮮豔度</string>\n    <string name=\"saturation\">飽和度</string>\n    <string name=\"sharpen\">銳利化</string>\n    <string name=\"exposure\">曝光度</string>\n    <string name=\"center\">置中</string>\n    <string name=\"color\">色彩</string>\n    <string name=\"start\">開始</string>\n    <string name=\"end\">結束</string>\n    <string name=\"bilateral_blur\">雙側模糊</string>\n    <string name=\"vignette\">漸暈</string>\n    <string name=\"box_blur\">方框模糊</string>\n    <string name=\"sepia\">復古</string>\n    <string name=\"clarendon\">Clarendon</string>\n    <string name=\"one977\">1977</string>\n    <string name=\"aden\">Aden</string>\n    <string name=\"reset\">重置</string>\n    <string name=\"crop\">裁剪</string>\n    <string name=\"normal\">普通</string>\n    <plurals name=\"views_count\">\n        <item quantity=\"other\">%d 次觀看</item>\n    </plurals>\n    <plurals name=\"stories_count\">\n        <item quantity=\"other\">%s 個動態</item>\n    </plurals>\n    <string name=\"details\">詳情</string>\n    <string name=\"title\">標題</string>\n    <string name=\"members\">成員</string>\n    <string name=\"admin\">管理員</string>\n    <string name=\"inviter\">邀請人</string>\n    <string name=\"mute_messages\">將訊息靜音</string>\n    <string name=\"mute_mentions\">將提及靜音</string>\n    <string name=\"add_members\">新增成員</string>\n    <string name=\"search\">搜尋</string>\n    <string name=\"done\">完成</string>\n    <string name=\"dms_action_make_admin\">設為管理員</string>\n    <string name=\"dms_action_remove_admin\">移除管理員權限</string>\n    <string name=\"edit_unsuccessful\">編輯失敗</string>\n    <string name=\"message\">訊息</string>\n    <string name=\"tap_to_remove\">點擊即可刪除</string>\n    <string name=\"forward\">轉發</string>\n    <string name=\"forward_outgoing\">您轉發了一條訊息</string>\n    <string name=\"forward_incoming\">轉發了一條訊息</string>\n    <string name=\"add\">新增</string>\n    <string name=\"send\">發送</string>\n    <string name=\"replying_to_yourself\">回覆給自己</string>\n    <string name=\"replying_to_user\">回覆 %s</string>\n    <string name=\"replied_to_yourself\">您回覆了自己</string>\n    <string name=\"replied_you\">您回覆了</string>\n    <string name=\"replied_you_group\">您回覆了 %s</string>\n    <string name=\"replied_group\">回覆了 %s</string>\n    <string name=\"replied_to_you\">回覆給您</string>\n    <string name=\"replied_to_themself\">回覆給他自己</string>\n    <string name=\"reacted_story_outgoing\">你對對方的限時動態傳達了心情</string>\n    <string name=\"reacted_story_incoming\">對你的限時動態傳達了心情</string>\n    <string name=\"mentioned_story_outgoing\">你在限時動態中提及了他</string>\n    <string name=\"mentioned_story_incoming\">在限時動態中提及了你</string>\n    <string name=\"replied_story_outgoing\">你回覆了對方的限時動態</string>\n    <string name=\"replied_story_incoming\">回覆了你的限時動態</string>\n    <string name=\"raven_image_expired\">圖片已過期</string>\n    <string name=\"raven_image_info\">圖片會在開啟後過期</string>\n    <string name=\"raven_video_expired\">影片已過期</string>\n    <string name=\"raven_video_info\">影片會在開啟後過期</string>\n    <string name=\"raven_msg_expired\">訊息已過期</string>\n    <string name=\"raven_msg_info\">訊息會在開啟後過期</string>\n    <string name=\"story_share\">\\@%s 的限時動態</string>\n    <string name=\"story_share_highlight\">\\@%s 的精選限時動態</string>\n    <string name=\"photo\">照片</string>\n    <string name=\"video\">影片</string>\n    <string name=\"voice_message\">語音訊息</string>\n    <string name=\"post\">貼文</string>\n    <string name=\"approval_required_for_new_members\">需批准才能加入</string>\n    <string name=\"requests\">請求</string>\n    <string name=\"admins_only\">限管理員可用</string>\n    <string name=\"added_by\">由 %s 加入</string>\n    <string name=\"admin_approval_required\">需要管理員批准</string>\n    <string name=\"admin_approval_required_description\">添加新成員至此群組需管理員批准</string>\n    <string name=\"dms_action_end\">結束聊天</string>\n    <string name=\"dms_action_end_question\">結束聊天?</string>\n    <string name=\"dms_action_end_description\">將踢出群組中的所有成員。他們仍然可以查看聊天記錄。</string>\n    <string name=\"pending_requests\">待處理的請求</string>\n    <string name=\"accept_request_from_user\">接受來自 %1s (%2s) 的請求？</string>\n    <string name=\"decline\">拒絕</string>\n    <string name=\"accept\">接受</string>\n    <string name=\"you\">您</string>\n    <string name=\"no_pending_requests\">無待處理的請求</string>\n    <string name=\"checking_for_new_messages\">正在檢查新訊息</string>\n    <string name=\"pref_category_stories\">限時動態</string>\n    <string name=\"pref_category_dm\">私訊</string>\n    <string name=\"pref_category_notifications\">通知</string>\n    <string name=\"pref_category_post\">貼文</string>\n    <string name=\"enable_dm_notifications\">啟用私訊通知</string>\n    <string name=\"enable_dm_auto_refesh\">自動刷新私訊</string>\n    <string name=\"auto_refresh_every\">自動刷新間隔</string>\n    <string name=\"secs\">秒</string>\n    <string name=\"mins\">分</string>\n    <string name=\"search_giphy\">搜尋 GIPHY</string>\n    <string name=\"generic_null_response\">回應為空！</string>\n    <string name=\"generic_not_ok_response\">回應狀態錯誤！</string>\n    <string name=\"generic_failed_request\">請求失敗！</string>\n    <string name=\"hint_keyword\">關鍵字</string>\n    <string name=\"toggle_keyword_filter\">啟用關鍵字過濾</string>\n    <string name=\"edit_keyword_filter\">編輯關鍵字過濾</string>\n    <string name=\"added_keywords\">已新增關鍵字：%s 至過濾列表</string>\n    <string name=\"removed_keywords\">已從過濾列表移除關鍵字：%s</string>\n    <string name=\"marked_as_seen\">標記為已讀</string>\n    <string name=\"delete_unsuccessful\">刪除失敗</string>\n    <string name=\"throttle_error\">您發送的 API 請求過多。請稍後再試。</string>\n    <string name=\"error\">錯誤</string>\n    <string name=\"account_logged_out\">此帳戶已被登出。</string>\n    <string name=\"login_required\">需要登入！</string>\n    <string name=\"inactive_user\">此用戶已被停用！</string>\n    <string name=\"crash_report_subject\">Barinsta 錯誤報告</string>\n    <string name=\"crash_report_title\">選擇任一電子郵件應用程式回傳錯誤報告</string>\n    <string name=\"not_found\">未找到所要的內容！</string>\n    <string name=\"rate_limit\">Your IP has been rate limited by Instagram. &lt;a href=\\\"https://barinsta.austinhuang.me/en/latest/faq.html#ratelimits\\\"&gt;Learn more.&lt;/a&gt;</string>\n    <string name=\"skip_update\">跳過此更新</string>\n    <string name=\"on_latest_version\">已經是最新版本</string>\n    <string name=\"tab_order\">頁面順序</string>\n    <string name=\"other_tabs\">其他頁面</string>\n    <string name=\"tab_order_start_next_launch\">頁面順序將在下次啟動時生效</string>\n    <string name=\"dm_remove_warning\">若您如此儲存，所有私訊功能將在下次啟動後被關閉</string>\n    <string name=\"copy_caption\">複製說明</string>\n    <string name=\"copy_reply\">複製回覆</string>\n    <string name=\"restore\">還原</string>\n    <string name=\"backup\">備份</string>\n    <string name=\"dir_select_default_message\">Select a folder where Barinsta can store downloads and temporary files.\\n\\nYou can change this later in More &gt; Settings &gt; Downloads.</string>\n    <string name=\"dir_select_reselect_message\">Android has changed the way apps can access files and directories on storage. Currently Barinsta does not have permission to access the following folder:</string>\n    <string name=\"dir_select_permission_revoked_message\">Permissions for the previously selected folder were revoked by the system:</string>\n    <string name=\"dir_select_folder_not_exist\">The previously selected folder does not exist now:</string>\n    <string name=\"dir_select_message2\">Re-select the directory or select a new directory by clicking the button below.</string>\n    <string name=\"select_a_folder\">未選取任何資料夾</string>\n    <string name=\"dir_select_no_download_folder\">Please choose a directory from your storage, not a category on the sidebar.\\n(%s)</string>\n    <string name=\"dir_select_success_message\">Success! Please wait. Starting app…</string>\n    <string name=\"barinsta_folder\">Barinsta 資料夾</string>\n    <string name=\"top\">熱門</string>\n    <string name=\"recent\">最近</string>\n    <string name=\"clear\">清空</string>\n    <string name=\"no_external_map_app\">找不到可用的地圖應用程式</string>\n    <string name=\"click_to_show_full\">點選以顯示完整的按讚數量</string>\n    <string name=\"no_profile_pic_found\">找不到大頭貼照</string>\n    <string name=\"swipe_up_confirmation\">您確定要打開此連結嗎？</string>\n    <string name=\"sending\">正在發送…</string>\n    <string name=\"share_via_dm\">透過私訊分享</string>\n    <string name=\"share_link\">分享連結</string>\n    <string name=\"slide_to_cancel\">滑動取消發送</string>\n    <string name=\"disable_screen_transitions\">Disable screen transitions</string>\n    <string name=\"invalid_format\">Invalid format</string>\n    <string name=\"no_directory_picker_activity\">Barinsta cannot launch Android\\'s file manager. Please make sure it is installed and enabled on your device.</string>\n    <string name=\"story_stickers\">貼圖</string>\n    <string name=\"story_list\">限時動態列表</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/backup_descriptor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<full-backup-content>\n\n</full-backup-content>\n"
  },
  {
    "path": "app/src/main/res/xml/provider_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <external-path\n        name=\"external_files\"\n        path=\".\" />\n</paths>"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/LiveDataTestUtil.kt",
    "content": "package awais.instagrabber\n\nimport androidx.annotation.VisibleForTesting\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.Observer\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeoutException\n\n@VisibleForTesting(otherwise = VisibleForTesting.NONE)\nfun <T> LiveData<T>.getOrAwaitValue(\n    time: Long = 2,\n    timeUnit: TimeUnit = TimeUnit.SECONDS,\n    afterObserve: () -> Unit = {}\n): T {\n    var data: T? = null\n    val latch = CountDownLatch(1)\n    val observer = object : Observer<T> {\n        override fun onChanged(o: T?) {\n            data = o\n            latch.countDown()\n            this@getOrAwaitValue.removeObserver(this)\n        }\n    }\n    this.observeForever(observer)\n\n    try {\n        afterObserve.invoke()\n\n        // Don't wait indefinitely if the LiveData is not set.\n        if (!latch.await(time, timeUnit)) {\n            throw TimeoutException(\"LiveData value was never set.\")\n        }\n\n    } finally {\n        this.removeObserver(observer)\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    return data as T\n}\n"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/MainCoroutineScopeRule.kt",
    "content": "/*\n * Copyright (C) 2019 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage awais.instagrabber\n\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport kotlinx.coroutines.test.TestCoroutineDispatcher\nimport kotlinx.coroutines.test.TestCoroutineScope\nimport kotlinx.coroutines.test.resetMain\nimport kotlinx.coroutines.test.setMain\nimport org.junit.rules.TestWatcher\nimport org.junit.runner.Description\n\n/**\n * MainCoroutineRule installs a TestCoroutineDispatcher for Disptachers.Main.\n *\n * Since it extends TestCoroutineScope, you can directly launch coroutines on the MainCoroutineRule\n * as a [CoroutineScope]:\n *\n * ```\n * mainCoroutineRule.launch { aTestCoroutine() }\n * ```\n *\n * All coroutines started on [MainCoroutineScopeRule] must complete (including timeouts) before the test\n * finishes, or it will throw an exception.\n *\n * When using MainCoroutineRule you should always invoke runBlockingTest on it to avoid creating two\n * instances of [TestCoroutineDispatcher] or [TestCoroutineScope] in your test:\n *\n * ```\n * @Test\n * fun usingRunBlockingTest() = mainCoroutineRule.runBlockingTest {\n *     aTestCoroutine()\n * }\n * ```\n *\n * You may call [DelayController] methods on [MainCoroutineScopeRule] and they will control the\n * virtual-clock.\n *\n * ```\n * mainCoroutineRule.pauseDispatcher()\n * // do some coroutines\n * mainCoroutineRule.advanceUntilIdle() // run all pending coroutines until the dispatcher is idle\n * ```\n *\n * By default, [MainCoroutineScopeRule] will be in a *resumed* state.\n *\n * @param dispatcher if provided, this [TestCoroutineDispatcher] will be used.\n */\n@ExperimentalCoroutinesApi\nclass MainCoroutineScopeRule(val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) :\n    TestWatcher(),\n    TestCoroutineScope by TestCoroutineScope(dispatcher) {\n    override fun starting(description: Description?) {\n        super.starting(description)\n        // If your codebase allows the injection of other dispatchers like\n        // Dispatchers.Default and Dispatchers.IO, consider injecting all of them here\n        // and renaming this class to `CoroutineScopeRule`\n        //\n        // All injected dispatchers in a test should point to a single instance of\n        // TestCoroutineDispatcher.\n        Dispatchers.setMain(dispatcher)\n    }\n\n    override fun finished(description: Description?) {\n        super.finished(description)\n        cleanupTestCoroutines()\n        Dispatchers.resetMain()\n    }\n}\n"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/common/Adapters.kt",
    "content": "package awais.instagrabber.common\n\nimport awais.instagrabber.db.dao.AccountDao\nimport awais.instagrabber.db.dao.FavoriteDao\nimport awais.instagrabber.db.entities.Account\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.repositories.*\nimport awais.instagrabber.repositories.responses.*\nimport awais.instagrabber.repositories.responses.directmessages.*\nimport awais.instagrabber.repositories.responses.stories.*\n\nopen class UserServiceAdapter : UserService {\n    override suspend fun getUserInfo(uid: Long): WrappedUser {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getUsernameInfo(username: String): WrappedUser {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getUserFriendship(uid: Long): FriendshipStatus = FriendshipStatus()\n\n    override suspend fun search(timezoneOffset: Float, query: String): UserSearchResponse {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class FriendshipServiceAdapter : FriendshipService {\n    override suspend fun change(action: String, id: Long, form: Map<String, String>): FriendshipChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun toggleRestrict(action: String, form: Map<String, String>): FriendshipRestrictResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getList(userId: Long, type: String, queryParams: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun changeMute(action: String, form: Map<String, String>): FriendshipChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class StoriesServiceAdapter : StoriesService {\n    override suspend fun fetch(mediaId: Long): StoryMediaResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getFeedStories(): ReelsTrayResponse? {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchHighlights(uid: Long): ReelsTrayResponse? {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchArchive(queryParams: Map<String, String>): ArchiveResponse? {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getReelsMedia(id: String): ReelsMediaResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getStories(type: String, id: String): ReelsResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getUserStories(id: Long): ReelsResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun respondToSticker(storyId: Long, stickerId: Long, action: String, form: Map<String, String>): StoryStickerResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun seen(queryParams: Map<String, String>, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class MediaServiceAdapter : MediaService {\n    override suspend fun fetch(mediaId: Long): MediaInfoResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchLikes(mediaId: String, action: String): LikersResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun action(action: String, mediaId: String, signedForm: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun editCaption(mediaId: String, signedForm: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun translate(form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun uploadFinish(retryContext: String, queryParams: Map<String, String>, signedForm: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun delete(mediaId: String, mediaType: String, signedForm: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun archive(mediaId: String, signedForm: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class GraphQLServiceAdapter : GraphQLService {\n    override suspend fun fetch(queryParams: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getUser(username: String): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getPost(shortcode: String): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getTag(tag: String): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun getLocation(locationId: Long): String {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class AccountDaoAdapter : AccountDao {\n    override suspend fun getAllAccounts(): List<Account> {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun findAccountByUid(uid: String): Account? {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun insertAccounts(vararg accounts: Account) {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun updateAccounts(vararg accounts: Account) {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun deleteAccounts(vararg accounts: Account) {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun deleteAllAccounts() {\n        TODO(\"Not yet implemented\")\n    }\n}\n\nopen class FavoriteDaoAdapter : FavoriteDao {\n    override suspend fun getAllFavorites(): List<Favorite> = emptyList()\n\n    override suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite? = null\n\n    override suspend fun insertFavorites(vararg favorites: Favorite) {}\n\n    override suspend fun updateFavorites(vararg favorites: Favorite) {}\n\n    override suspend fun deleteFavorites(vararg favorites: Favorite) {}\n\n    override suspend fun deleteAllFavorites() {}\n}\n\nopen class DirectMessagesServiceAdapter: DirectMessagesService {\n    override suspend fun fetchInbox(queryMap: Map<String, String>): DirectInboxResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchPendingInbox(queryMap: Map<String, String>): DirectInboxResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchThread(threadId: String, queryMap: Map<String, String>): DirectThreadFeedResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun fetchUnseenCount(): DirectBadgeCount {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun broadcast(item: String, signedForm: Map<String, String>): DirectThreadBroadcastResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun addUsers(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun removeUsers(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun updateTitle(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun addAdmins(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun removeAdmins(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun deleteItem(threadId: String, itemId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun rankedRecipients(queryMap: Map<String, String>): RankedRecipientsResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun forward(form: Map<String, String>): DirectThreadBroadcastResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun createThread(signedForm: Map<String, String>): DirectThread {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun mute(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun unmute(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun muteMentions(threadId: String, form: Map<String, String?>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun unmuteMentions(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun participantRequests(threadId: String, pageSize: Int, cursor: String?): DirectThreadParticipantRequestsResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun approveParticipantRequests(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun declineParticipantRequests(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun approvalRequired(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun approvalNotRequired(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun leave(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun end(threadId: String, form: Map<String, String>): DirectThreadDetailsChangeResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun approveRequest(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun declineRequest(threadId: String, form: Map<String, String>): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override suspend fun markItemSeen(threadId: String, itemId: String, form: Map<String, String>): DirectItemSeenResponse {\n        TODO(\"Not yet implemented\")\n    }\n\n}"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/utils/CubicInterpolationTest.java",
    "content": "package awais.instagrabber.utils;\n\nimport org.junit.jupiter.api.Test;\n\nclass CubicInterpolationTest {\n\n    @Test\n    void interpolate() {\n        final float[] array = {0,\n                0,\n                0,\n                0,\n                0.020099867f,\n                0.26693913f,\n                0.29802227f,\n                0.41319302f,\n                0.63605154f,\n                0.55332285f,\n                0.9193651f,\n                0.9632098f,\n                0.538488f,\n                0.4350556f,\n                0.18891974f,\n                0.89930296f,\n                0.89224446f,\n                0.31689966f,\n                0.10953838f,\n                0.12687835f,\n                0.10990722f,\n                1.4566885f,\n                1.7016107f,\n                0.9643246f,\n                0.48731846f,\n                1.0107778f,\n                0.7347803f,\n                0.5262502f,\n                0.54368f,\n                0.44617367f,\n                0.14939539f,\n                1.214883f,\n                1.6565621f,\n                2.1008852f,\n                1.4302688f,\n                2.00104f,\n                0.62863034f,\n                0.10548139f,\n                0.12392626f,\n                0.09700036f,\n                0.22292629f,\n                1.9768263f,\n                1.0130078f,\n                0.14681084f,\n                0.116178185f,\n                1.8114564f,\n                2.68573f,\n                2.4381645f};\n        final CubicInterpolation cubicInterpolation = new CubicInterpolation(array);\n        final float interpolate = cubicInterpolation.interpolate(5.25f);\n        System.out.println(interpolate);\n    }\n}"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/utils/DownloadUtilsTest.java",
    "content": "package awais.instagrabber.utils;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nclass DownloadUtilsTest {\n\n    @Test\n    void getFileExtensionFromUrl() {\n        String extension = DownloadUtils.getFileExtensionFromUrl(\n                \"https://scontent-nrt1-1.cdninstagram.com/v/t51.2885-15/e35/119960990_327287531705166_2607097160272969998_n.jpg?_nc_ht=scontent-nrt1-1.cdninstagram.com&_nc_cat=1&_nc_ohc=Wu8uveQFmwkAX9qy7kN&_nc_tp=18&oh=f2f93b9eecc1b7dee2b43252c9cf451e&oe=5F9418F4\");\n        Assertions.assertEquals(\"jpg\", extension);\n        extension = DownloadUtils.getFileExtensionFromUrl(\n                \"https://scontent-nrt1-1.cdninstagram.com/v/t50.2886-16/119894911_194292172085874_5977032350348410166_n.mp4?_nc_ht=scontent-nrt1-1.cdninstagram.com&_nc_cat=106&_nc_ohc=nLErTQ4bU0kAX9XJ4Of&oe=5F6C6B8C&oh=682532b4a941181a6b89736a9002b890\");\n        Assertions.assertEquals(\"mp4\", extension);\n    }\n\n    @Test\n    void testDownloadPercent() {\n        // int position = 3;\n        int total = 6;\n        // int currentPercent = 100;\n        // ans: (((100 * 1/ 6) ) * (100 / 100)) + (100 * (position - 1)) / total\n        for (int position = 1; position <= total; position++) {\n            for (float currentPercent = 1; currentPercent <= 100; currentPercent++) {\n                float totalPercent = (100f * (position - 1) / total) + (1f / total) * (currentPercent);\n                System.out.println(totalPercent);\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/utils/IntentUtilsTest.java",
    "content": "// java.lang.RuntimeException: Method parse in android.net.Uri not mocked.\n// See http://g.co/androidstudio/not-mocked for details.\n\npackage awais.instagrabber.utils;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport awais.instagrabber.models.IntentModel;\nimport awais.instagrabber.models.enums.IntentModelType;\n\nclass IntentUtilsTest {\n\n    @Test\n    void getIntentFromUrl() {\n        IntentModel intent = IntentUtils.parseUrl(\"https://instagr.am/austinhuang.me\");\n        Assertions.assertEquals(new IntentModel(IntentModelType.USERNAME, \"austinhuang.me\"), intent);\n        intent = IntentUtils.parseUrl(\"https://www.instagr.am/_u/austinhuang.me\");\n        Assertions.assertEquals(new IntentModel(IntentModelType.USERNAME, \"austinhuang.me\"), intent);\n        intent = IntentUtils.parseUrl(\"https://instagram.com/p/BmjKdkxjzO7/\");\n        Assertions.assertEquals(new IntentModel(IntentModelType.POST, \"BmjKdkxjzO7\"), intent);\n        intent = IntentUtils.parseUrl(\"https://www.instagram.com/explore/tags/metrodemontreal/\");\n        Assertions.assertEquals(new IntentModel(IntentModelType.HASHTAG, \"metrodemontreal\"), intent);\n        intent = IntentUtils.parseUrl(\"http://www.instagram.com/explore/locations/538444610/abcde\");\n        Assertions.assertEquals(new IntentModel(IntentModelType.LOCATION, \"538444610\"), intent);\n        // todo: reel and igtv test cases that are sfw and preferably n i c e\n    }\n}"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/utils/TextUtilsTest.java",
    "content": "package awais.instagrabber.utils;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nclass TextUtilsTest {\n\n    @Test\n    void testMillisToTimeString() {\n        String timeString = TextUtils.millisToTimeString(18000000);\n        Assertions.assertEquals(\"05:00:00\", timeString);\n\n        timeString = TextUtils.millisToTimeString(300000);\n        Assertions.assertEquals(\"05:00\", timeString);\n\n        timeString = TextUtils.millisToTimeString(300000, true);\n        Assertions.assertEquals(\"00:05:00\", timeString);\n    }\n\n    @Test\n    void testShortcodeConversion() {\n        long conversion = TextUtils.shortcodeToId(\"CA0YnOonSfS\");\n        Assertions.assertEquals(2320587956892280786L, conversion);\n        conversion = TextUtils.shortcodeToId(\"B_7n8mblwx6gv1ZaNvA5ZhAs2qslMnRiMMYW1c0\");\n        Assertions.assertEquals(2304611322577751162L, conversion);\n    }\n}"
  },
  {
    "path": "app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt",
    "content": "package awais.instagrabber.viewmodels\n\nimport androidx.arch.core.executor.testing.InstantTaskExecutorRule\nimport androidx.lifecycle.SavedStateHandle\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport awais.instagrabber.MainCoroutineScopeRule\nimport awais.instagrabber.common.*\nimport awais.instagrabber.db.datasources.FavoriteDataSource\nimport awais.instagrabber.db.entities.Favorite\nimport awais.instagrabber.db.repositories.FavoriteRepository\nimport awais.instagrabber.getOrAwaitValue\nimport awais.instagrabber.models.Resource\nimport awais.instagrabber.models.enums.FavoriteType\nimport awais.instagrabber.repositories.requests.StoryViewerOptions\nimport awais.instagrabber.repositories.responses.FriendshipStatus\nimport awais.instagrabber.repositories.responses.User\nimport awais.instagrabber.repositories.responses.stories.Story\nimport awais.instagrabber.webservices.*\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport org.json.JSONException\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.jupiter.api.Assertions.*\nimport org.junit.runner.RunWith\nimport java.time.LocalDateTime\n\n@RunWith(AndroidJUnit4::class)\ninternal class ProfileFragmentViewModelTest {\n\n    @get:Rule\n    var instantExecutorRule = InstantTaskExecutorRule()\n\n    @ExperimentalCoroutinesApi\n    @get:Rule\n    val coroutineScope = MainCoroutineScopeRule()\n\n    private lateinit var testPublicUser: User\n    private lateinit var testPublicUser1: User\n\n    private val csrfToken = \"csrfToken\"\n    private val deviceUuid = \"deviceUuid\"\n\n    @Before\n    fun setup() {\n        testPublicUser = User(\n            pk = 100,\n            username = \"test\",\n            fullName = \"Test user\"\n        )\n        testPublicUser1 = User(\n            pk = 101,\n            username = \"test1\",\n            fullName = \"Test1 user1\"\n        )\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `no state username and null current user`() {\n        val viewModel = ProfileFragmentViewModel(\n            SavedStateHandle(),\n            null,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            GraphQLRepository(GraphQLServiceAdapter()),\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        viewModel.setCurrentUser(Resource.success(null))\n        assertNull(viewModel.profile.getOrAwaitValue().data)\n        assertEquals(\"\", viewModel.username.getOrAwaitValue())\n        viewModel.setCurrentUser(Resource.success(null))\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `no state username with current user provided`() {\n        val viewModel = ProfileFragmentViewModel(\n            SavedStateHandle(),\n            csrfToken,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            GraphQLRepository(GraphQLServiceAdapter()),\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        assertNull(viewModel.profile.getOrAwaitValue().data)\n        val user = User()\n        viewModel.setCurrentUser(Resource.success(user))\n        assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(user, profile.data)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `state username without '@' and no current user`() {\n        // username without `@`\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        testPublicUsernameNoCurrentUserCommon(state)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `state username with '@' and no current user`() {\n        // username with `@`\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to \"@${testPublicUser.username}\"\n            )\n        )\n        testPublicUsernameNoCurrentUserCommon(state)\n    }\n\n    @ExperimentalCoroutinesApi\n    private fun testPublicUsernameNoCurrentUserCommon(state: SavedStateHandle) {\n        val graphQLRepository = object : GraphQLRepository(GraphQLServiceAdapter()) {\n            override suspend fun fetchUser(username: String): User = testPublicUser\n        }\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            null,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            graphQLRepository,\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(null))\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser, profile.data)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `state username without '@' and current user provided`() {\n        // username without `@`\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        testPublicUsernameCurrentUserCommon(state)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `state username with '@' and current user provided`() {\n        // username with `@`\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to \"@${testPublicUser.username}\"\n            )\n        )\n        testPublicUsernameCurrentUserCommon(state)\n    }\n\n    @ExperimentalCoroutinesApi\n    private fun testPublicUsernameCurrentUserCommon(state: SavedStateHandle) {\n        val friendshipStatus = FriendshipStatus(following = true)\n        val userRepository = object : UserRepository(UserServiceAdapter()) {\n            override suspend fun getUsernameInfo(username: String): User = testPublicUser\n            override suspend fun getUserFriendship(uid: Long): FriendshipStatus = friendshipStatus\n        }\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            csrfToken,\n            deviceUuid,\n            userRepository,\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            GraphQLRepository(GraphQLServiceAdapter()),\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(User()))\n        assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser, profile.data)\n        assertEquals(friendshipStatus, profile.data?.friendshipStatus)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `state username changes`() {\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        val graphQLRepository = object : GraphQLRepository(GraphQLServiceAdapter()) {\n            override suspend fun fetchUser(username: String): User = when (username) {\n                testPublicUser.username -> testPublicUser\n                testPublicUser1.username -> testPublicUser1\n                else -> throw JSONException(\"\")\n            }\n        }\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            null,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            graphQLRepository,\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(null))\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser, profile.data)\n        state.set(\"username\", testPublicUser1.username)\n        profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser1, profile.data)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `should update favorite in db if fetched user is a favorite`() {\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        val favorite = Favorite(\n            1,\n            testPublicUser.username,\n            FavoriteType.USER,\n            testPublicUser.username,\n            \"test url\",\n            LocalDateTime.now()\n        )\n        val graphQLRepository = object : GraphQLRepository(GraphQLServiceAdapter()) {\n            override suspend fun fetchUser(username: String): User = testPublicUser\n        }\n        var updateFavoriteCalled = false\n        val favoriteRepository = FavoriteRepository(FavoriteDataSource(object : FavoriteDaoAdapter() {\n            override suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite = favorite\n            override suspend fun updateFavorites(vararg favorites: Favorite) {\n                updateFavoriteCalled = true\n            }\n        }))\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            null,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            graphQLRepository,\n            favoriteRepository,\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(null))\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(true, viewModel.isFavorite.getOrAwaitValue())\n        assertTrue(updateFavoriteCalled)\n    }\n\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `should fetch user stories and highlights when logged in`() {\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        val testUserStories = Story()\n        val testUserHighlights = listOf(Story())\n        val userRepository = object : UserRepository(UserServiceAdapter()) {\n            override suspend fun getUsernameInfo(username: String): User = testPublicUser\n        }\n        val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) {\n            override suspend fun getStories(options: StoryViewerOptions): Story = testUserStories\n            override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights\n        }\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            csrfToken,\n            deviceUuid,\n            userRepository,\n            FriendshipRepository(FriendshipServiceAdapter()),\n            storiesRepository,\n            MediaRepository(MediaServiceAdapter()),\n            GraphQLRepository(GraphQLServiceAdapter()),\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(User()))\n        assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        var userStories = viewModel.userStories.getOrAwaitValue()\n        while (userStories.status == Resource.Status.LOADING) {\n            userStories = viewModel.userStories.getOrAwaitValue()\n        }\n        assertEquals(testUserStories, userStories.data)\n        var userHighlights = viewModel.userHighlights.getOrAwaitValue()\n        while (userHighlights.status == Resource.Status.LOADING) {\n            userHighlights = viewModel.userHighlights.getOrAwaitValue()\n        }\n        assertEquals(testUserHighlights, userHighlights.data)\n    }\n\n    @ExperimentalCoroutinesApi\n    @Test\n    fun `should refresh correctly`() {\n        val state = SavedStateHandle(\n            mutableMapOf<String, Any?>(\n                \"username\" to testPublicUser.username\n            )\n        )\n        val graphQLRepository = object : GraphQLRepository(GraphQLServiceAdapter()) {\n            override suspend fun fetchUser(username: String): User = testPublicUser\n        }\n        val viewModel = ProfileFragmentViewModel(\n            state,\n            null,\n            deviceUuid,\n            UserRepository(UserServiceAdapter()),\n            FriendshipRepository(FriendshipServiceAdapter()),\n            StoriesRepository(StoriesServiceAdapter()),\n            MediaRepository(MediaServiceAdapter()),\n            graphQLRepository,\n            FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),\n            DirectMessagesRepository(DirectMessagesServiceAdapter()),\n            null,\n            coroutineScope.dispatcher,\n        )\n        viewModel.setCurrentUser(Resource.success(null))\n        assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())\n        var profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser, profile.data)\n        testPublicUser = testPublicUser.copy(biography = \"new bio\")\n        viewModel.refresh()\n        profile = viewModel.profile.getOrAwaitValue()\n        while (profile.status == Resource.Status.LOADING) {\n            profile = viewModel.profile.getOrAwaitValue()\n        }\n        assertEquals(testPublicUser, profile.data)\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    ext{\n        kotlin_version = '1.5.21'\n        nav_version = \"2.4.0-alpha04\"\n    }\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath \"androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "crowdin.yml",
    "content": "files:\n  - source: '/app/src/main/res/values/[arrays][strings][!styles]'\n    translation: /app/src/main/res/values-%two_letters_code%/%original_file_name%\n  - source: '/app/src/github/res/values/[arrays][strings][!styles]'\n    translation: /app/src/github/res/values-%two_letters_code%/%original_file_name%\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/32.txt",
    "content": "* You can now (un)follow/restrict/block people\n  * For this reason, \"Open in Instagram\" for following/follower list is removed\n* Link in bio is now under bio text (like the actual Instagram app)\n* Accounts with no posts will be indicated instead of having infinite refresh\n* When searching, all other buttons will be hidden to expand suggestion width\n* Remove AWAiS advertisement\n* Implement LGTM advice to improve stability"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/33.txt",
    "content": "* Full support on hashtags, including stories, \"profile picture\" (SD only), post count, and (un)following. Removed \"Hashtag:\" prefix.\n* Non-logged-in users now have an \"add/remove from favorites\" button on the profile/hashtag page, alongside Quick Access.\n* Update checker will now have a F-Droid button\n* Updated Italian and Simplified Chinese translations\n* Crash reporter now directs to `instagrabber@austinhuang.me`\n* Adjusted grid size threshold at popular request\n* Adjusted post viewer component sizes (to prevent the buttons being squished downwards, but the exact outcome depends on device)\n* Fixed a bug where highlights of the viewed user gets carried to other users\n* Fixed a bug where mentions in feeds were parsed incorrectly\n* Removed Insta-Stalker (defunct) as an HD profile picture provider, existing users are moved to Instafullsize upon first run."
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/36.txt",
    "content": "* You can now see tagged/\"saved\" posts. Tagged will work without login.\n* You can now write comments, and reply/like/delete comments through the menu (by clicking the comment)\n    * Liked comments are shown with a pink background.\n* You can now share posts in the post viewer.\n* You can now respond to green/red polls in stories (you also get to see results before voting)\n* You can now open Spotify if a story has linked it (non-playing ones)\n* Geotag viewing/searching support\n* Search field is now empty if you're viewing your own profile while logged in\n* Post like counts are now displayed (The button texts are slightly reduced to accomodate @world_record_egg) (logged in only)\n* Mute buttons are changed to display the current status (i.e. muted icon when muted, sound icon when not muted)\n* Added some new user guidance stuff\n* Fixed bugs related to the \"Downloaded\" icon\n* Fixed a bug relating to viewing feed stories\n* Fixed a bug where scrolling in full screen causes it to quit full screen"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/37.txt",
    "content": "* You can now respond to question boxes in stories\n* You can now check out story mentions (except non-Spotify music stickers, but you can hear them by playing the story)\n    * Above 2 does not apply to highlights\n* You can now click on locations from feed\n* Fixed a bug where you cannot access your own follower/following list when you are private and have no posts"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/38.txt",
    "content": "* You can now respond to quizzes, plus you get to see correct answer beforehand (FYI: I will not support music questions)\n* Fixed feed story fetch approach"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/39.txt",
    "content": "* You can now use discover by topics\n* You can now see posts you liked (it's in your own profile, also has a limit of 300)\n* Some DM improvements\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/40.txt",
    "content": "* You can now send text DMs\n* You can now reply DM to stories\n* DM title and (some) members are now shown on DM screen\n* Seen stories (API) are now displayed transparently\n* Story creation date is now shown on the story screen\n* A new setting allows you to mark stories as seen (Default disabled)\n* Fixed a bug related to the favourite button on profile\n* Fixed a bug where liked/saved status does not carry over across slides"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/41.txt",
    "content": "* You can now view public stories and highlights WITHOUT logging in, but only if you enable it in Settings\n* Originally, viewing HD avatars without logging in automatically uses Instadp, now you have to enable it in Settings as well\n  * Consent is important! A button now tells you\n* Highlights now support showing sticker infos"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/42.txt",
    "content": "The Answer to the Ultimate Question of Life, the Universe, and Everything is 42.\n\n* Implements #36 and #37, both massively improve the feed stories bar\n* Story viewer will tell you to slow down if you scroll across users too fast (Instead of launching another window)\n* Fixes a bug where emoji messages in DM crash the app\n* Improvements on login/logout\n\n(No new features in this version, sorry.)"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/43.txt",
    "content": "* Added AMOLED dark mode (#45, Thanks @ammargitham)\n* Various bug fixes, including\n  * #39 (Updater shows the next version instead of the latest version)\n  * Downloading stories still uses @... folder\n  * Story highlights not being shown\n  * Discover view being shown to non-logged-in users"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/44.txt",
    "content": "* New profile view for visiting profiles from mention/post/comment without losing all previous screens\n* Bug fixes (incl. #49 and #50)"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/45.txt",
    "content": "* You can now upload images in DM\n* You can now like messages\n* Various bug fixes"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/46.txt",
    "content": "* InstaGrabber will check activity/notifications every minute, when the app is on and when you're logged in\n* Changes to DM interfaces\n* You can now unsend messages\n* Adding favourite button on profile views\n* Enforcing a limit of 100 posts per download\n* Fix updater"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/47.txt",
    "content": "* You can now change the topic or leave a room, as well as see members\n* More optimizations on activity check (You can now disable it) and DM\n* Fix the inconsistent autoplay setting bug"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/48.txt",
    "content": "* Proper login support for those under Android 8\n* You are alerted before unfollowing a private account\n* Update checker can now be toggled\n* \"Swipe up\" story sticker type is now supported\n* Unread DM are now indicated\n* You can now mark DMs as seen, either automatically (settings) or manually\n* You can now kick DM members\n* \"Clip\" message type (basically sharing reels) is now supported\n* Bug fixes, see GitHub\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/49.txt",
    "content": "Bug fixes, see GitHub. This is the FINAL regular release for the old UI/codebase."
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/52.txt",
    "content": "## Features\n* We're now (officially) Barinsta! New name, new icon!\n* Total revamp of UI. Specifically, the profile/hashtag/location, feed, and discover tabs have been redone. Explore our new look!\n* You can now pick a feed layout and a theme!\n* You can now DM a user from their profile page!\n\n## Improvements\n* You can now skip to another user in story feed by clicking the skip buttons\n* URLs and emails in descrptions are now tappable\n* Downloaded file names now use shortcode instead of post ID\n* Support `FELIX_SHARE` DM message type\n\n## Fixes\n* Fix file corruption bug\n* Fix video autoplay & auto-mute issues"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/53.txt",
    "content": "* Resolve F-Droid compliance issues\n* Restore anonymous access to hashtags and locations\n* Various bug fixes\n\nBecause F-Droid skipped v19.0.0, the previous changelog is also provided here:\n\n## Features\n* We're now (officially) Barinsta! New name, new icon!\n* Total revamp of UI. Specifically, the profile/hashtag/location, feed, and discover tabs have been redone. Explore our new look!\n* You can now pick a feed layout and a theme!\n* You can now DM a user from their profile page!\n\n## Improvements\n* You can now skip to another user in story feed by clicking the skip buttons\n* URLs and emails in descrptions are now tappable\n* Downloaded file names now use shortcode instead of post ID\n* Support `FELIX_SHARE` DM message type\n\n## Fixes\n* Fix file corruption bug\n* Fix video autoplay & auto-mute issues"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/54.txt",
    "content": "* Bug fixes.\n* Catalan & Russian translations."
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/55.txt",
    "content": "Merry Christmas and Happy Holidays!\n\n* You can now see who liked a post (logged-in users only, long-click the like button)\n* You can now translate comments, captions, and bios (logged-in users only, through Instagram's API)\n  * Bio/Comment: Click bio once, then click \"translate\"\n  * Caption: Scroll down, then click \"translate\"\n* You can now respond to slider stickers on stories\n* Add Dutch and Hindi translations\n\n* Feed now uses the app endpoint, which allows you to see posts from followed hashtags\n* New profile details layout\n* Viewing comments and following/followers should be faster and more stable\n* The APK is now smaller (~10 MB → 6.3 MB)\n* Various bug fixes"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/56.txt",
    "content": "Happy new year! Added vertical story list, story archive, suggested users, and comment likes, as well as Slovak and Japanese translations. Fixed bugs.\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.0.4"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/57.txt",
    "content": "Preliminary support for live videos, as well as bug fixes.\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.0.5"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/60.txt",
    "content": "HD photo downloads, DM revamp, new profile layout, and more!\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.1.0"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/61.txt",
    "content": "New customizable bottom navigation bar, keyword filter, add username to file name, FLAG_SECURE, and more!\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.2.0"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/62.txt",
    "content": "Option to play video in background, favorites tab for anonymous users, and bug fixes.\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.2.1"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/63.txt",
    "content": "Search history, new comments/posts layout, and various improvements!\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.2.2"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/64.txt",
    "content": "Sharing, download via SAF, and various improvements/bug fixes!\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.2.3"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/65.txt",
    "content": "Revamped story viewer. You can now share stories by DM. The rest are bug fixes and various behind-the-scene improvements.\n\nFor details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.2.4"
  },
  {
    "path": "fastlane/metadata/android/en-US/full_description.txt",
    "content": "If you don't upload posts or stories on Instagram, but can't get rid of it because of people and contents, you now have an alternative: Barinsta is a beautiful client that allows you to browse and interact with Instagram, while giving you more options to control your data.\n\n* For those not having an account, you can access public profiles/hashtags/locations (depends on availability)!\n* For those logged in, Barinsta can achieve most viewing and interaction features, with extra features such as downloading posts/stories/avatars, copying texts, comparing followers/following, and more!\n\nExtra merits:\n\n* Prevents irrational decisions: Barinsta removes double-tap and requires you to click into posts to give it a conscious like. It also allows you to easily access posts you've liked through your own profile.\n* Allows customization: Pick your themes and layouts! You could even use grid layout for feed!\n* Allows viewing at your own pace: No need to \"hold\" a story or scroll endlessly! Zoom whenever you want!\n* Not a mod: Absolutely NO dependency of the actual Instagram app.\n* Free & Open Source Software: With the app's source code open for inspection, no need to worry about sketchy dealings!\n* Protects your privacy from friends: Barinsta allows you to read DMs and stories privately, without telling your friends!\n* Protects your privacy from Instagram: The app only requests what it needs to show you the content and what you opted for in the settings. No communicating with useless endpoints, and only minimal data is sent to Instagram.\n\n*** Please use this app responsibly. ***\n\nFor more info, visit https://barinsta.austinhuang.me.\n\n(We're previously known as InstaGrabber.)"
  },
  {
    "path": "fastlane/metadata/android/en-US/short_description.txt",
    "content": "Simple yet advanced Instagram client\n"
  },
  {
    "path": "fastlane/metadata/android/fr-FR/full_description.txt",
    "content": "Si vous ne mettez pas en ligne pas de publications ou de stories sur Instagram, mais que vous ne pouvez pas vous en débarrasser à cause des personnes et des contenus, vous avez maintenant une alternative : Barinsta est un beau client qui vous permet de naviguer et d'interagir avec Instagram, tout en vous offrant plus d'options pour contrôler vos données.\n\n* Pour ceux qui n'ont pas de compte, vous pouvez accéder aux profils publiques/hashtags/lieux !\n* Pour ceux qui sont connectés, Barinsta peut obtenir la plupart des fonctionnalités de visualisation et d'interaction, avec des fonctionnalités supplémentaires telles que le téléchargement de messages/stories/avatars, la copie de textes, la comparaison des abonnés/abonnements, et plus encore !\nAvantages supplémentaires :\n\n* Empêche les décisions irrationnelles : Barinsta supprime le double-tap et vous oblige à cliquer dans les publications pour leur donner un like consciencieux. Il vous permet également d'accéder facilement aux publications que vous avez aimées via votre propre profil.\n* Permet la personnalisation : choisissez vos thèmes et mises en page ! Vous pouvez même utiliser une disposition en grille pour le feed !\n* Permet de visionner à votre rythme : pas besoin de «tenir» une story ou de scroller sans fin ! Zoomez quand vous le souhaitez !\n* Ce n'est pas un mod : absolument AUCUNE dépendance de l'application Instagram réelle.\n* Logiciel libre et open source : avec le code source de l'application ouvert pour inspection, pas besoin de s'inquiéter d'affaires louches !\n* Protège votre vie privée de vos amis : Barinsta vous permet de lire des messages et des stories secrètement, sans que vos amis le sachent !\n* Protège votre vie privée d'Instagram : l'application ne demande que ce dont elle a besoin pour vous montrer le contenu et ce que vous avez choisi dans les paramètres. Aucune communication avec des endpoints inutiles et seules des données minimales sont envoyées à Instagram.\n*** S'il vous plait, utilisez cette application de manière responsable. ***\n\nPour plus d'informations, visiter https://barinsta.austinhuang.me.\n\n(Nous étions auparavant connus sous le nom d'InstaGrabber.)\n"
  },
  {
    "path": "fastlane/metadata/android/fr-FR/short_description.txt",
    "content": "Client Instagram simple mais avancé\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=9bb8bc05f562f2d42bdf1ba8db62f6b6fa1c3bf6c392228802cc7cb0578fe7e0\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.1.1-all.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "## For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n#\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx1024m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n#\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n#Wed Jun 30 00:40:18 JST 2021\norg.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\\=\"-Xmx2048M\" -XX:+UseParallelGC\nandroid.enableJetifier=true\nandroid.useAndroidX=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MSYS* | MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\n    \"config:base\"\n  ],\n  \"prConcurrentLimit\": 10,\n  \"prHourlyLimit\": 5\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\nrootProject.name = \"Barinsta\"\n"
  }
]